edwardtfn / HomeAssistant-Config

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

"Electricy price" template sensor not working? #8

Closed redlefloh closed 8 months ago

redlefloh commented 9 months ago

Hi @edwardtfn, is the "Electricy price" template sensor as you have it specified here still supposed to work? I copied it as is but unfortunately the sensor is "Unavailable" for me:

Screenshot 2023-12-10 at 23 28 59

even though the "Tibber Electricity Price" REST sensor works just fine?

Screenshot 2023-12-10 at 23 30 07

Is there anything I am missing or doing wrong? The YAML files are below, if you could maybe take a quick look? Thanks so much!


The "Tibber Electricity Price" sensor looks like this:

- sensor:
  - platform: rest
    name: Tibber Electricity Price
    unique_id: <a-unique-id>
    icon: mdi:currency-eur
    resource: https://api.tibber.com/v1-beta/gql
    method: POST
    scan_interval: 60
    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>
      Content-Type: application/json
      User-Agent: REST

The "Electricity Price" sensor looks like this:

- template:
  - sensor:
    - name: Electricity Price
      unique_id: <a-unique-id>
      unit_of_measurement: "€/kWh"
      icon: mdi:currency-eur
      state: >-
        {% if this.attributes.future_prices | default('unknown') in ['unknown', 'unavailable'] or (this.attributes.future_prices | default([]) | map(attribute='total') | list | count) < 1 %}
          unknown
        {% else %}
          {{ this.attributes.future_prices |  default([]) | map(attribute='total') | list | first | float(0) }}
        {% endif %}
      attributes:
        current: >-
          {% if state_attr('sensor.tibber_electricity_price', 'current') %}
            {{ state_attr('sensor.tibber_electricity_price', 'current') }}
          {% else %}
            [ ]
          {% endif %}
        today: >-
          {% if state_attr('sensor.tibber_electricity_price', 'today') %}
            {{ state_attr('sensor.tibber_electricity_price', 'today') }}
          {% else %}
            [ ]
          {% endif %}
        tomorrow: >-
          {% if state_attr('sensor.tibber_electricity_price', 'tomorrow') %}
            {{ state_attr('sensor.tibber_electricity_price', 'tomorrow') }}
          {% else %}
            [ ]
          {% endif %}
        all_prices: "{{ this.attributes.today | default([]) + this.attributes.tomorrow | default([]) }}"
        #today_count: "{{ this.attributes.today | default([]) | map(attribute='total') | list | count }}"
        #tomorrow_count: "{{ this.attributes.tomorrow | default([]) | map(attribute='total') | list | count }}"
        #all_prices_count: "{{ this.attributes.all_prices | default([]) | map(attribute='total') | list | count }}"
        min_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | min | float(0) | round(4) }}
          {% endif %}
        max_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | max | float(0) | round(4) }}
          {% endif %}
        avg_price: >-
          {% if this.attributes.today | default('unknown') in ['unknown','unavailable','none'] or (this.attributes.today | default([]) | map(attribute='total') | list | count) < 1 %}
            unknown
          {% elif (this.attributes.today | default([]) | map(attribute='total') | list | count) < 2 %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | max | float(0) | round(4) }}
          {% else %}
            {{ this.attributes.today | default([0]) | map(attribute='total') | list | average(0) | float(0) | round(4) }}
          {% endif %}
        price_level: >-
          {% if (this.attributes.current | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ this.attributes.current.level | default('unknown') | replace('_', ' ') | capitalize }}
          {% endif %}
        price_level_1d: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.avg_price | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        price_level_combined: >-
          {% set level1 = this.attributes.price_level_1d | default('unknown') %}
          {% set level3 = this.attributes.price_level | default('unknown') %}
          {% if level1 == level3 %}
            {{ level1 }}
          {% elif level1 in ['unknown','unavailable','none'] or level3 in ['unknown','unavailable','none'] %}
            unknown
          {% elif level1 == "Very cheap" %}
            {{ level3 }}
          {% elif level3 == "Very cheap" %}
            {{ level1 }}
          {% elif level1 == "Cheap" %}
            {{ level3 }}
          {% elif level3 == "Cheap" %}
            {{ level1 }}
          {% elif level1 == "Normal" %}
            {{ level3 }}
          {% elif level3 == "Normal" %}
            {{ level1 }}
          {% elif level1 == "Expensive" %}
            {{ level3 }}
          {% else %}
            {{ level1 }}
          {% endif %}
        is_below_average: >-
          {% if is_number(this.state) and is_number(this.attributes.avg_price) %}
            {{ (this.state | float(0) < this.attributes.avg_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_above_average: >-
          {% if is_number(this.state) and is_number(this.attributes.avg_price) %}
            {{ (this.state | float(0) > this.attributes.avg_price | float(0)) | lower}}
          {% else %}
            unknown
          {% endif %}
        is_at_min: >-
          {% if is_number(this.state) and is_number(this.attributes.min_price) %}
            {{ (this.state | float(0) <= this.attributes.min_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_at_max: >-
          {% if is_number(this.state) and is_number(this.attributes.max_price) %}
            {{ (this.state | float(0) >= this.attributes.max_price | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}
        is_close_to_min: >-
          {% if is_number(this.state) and is_number(this.attributes.min_price) %}
            {{ (this.state | float(0) <= (1.15 * this.attributes.min_price | float(0))) | lower }}
          {% else %}
            unknown
          {% endif %}
        future_prices: >-
          {% if (this.attributes.all_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.all_prices | default([])) | selectattr('startsAt', 'gt', (now() - timedelta(hours=1)) | string | replace(' ','T')) | list }}
          {% endif %}
        future_prices_totals: >-
          {% if (this.attributes.future_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices | default([])) | map(attribute='total') | list }}
          {% endif %}
        #future_prices_count: "{{ (this.attributes.future_prices_totals | default([])) | count }}"
        future_prices_min: |-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{  this.attributes.future_prices_totals | default([0]) | min | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_max: |-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_totals | default([0])) | max | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_avg: >-
          {% if this.attributes.future_prices_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_totals | default([0])) | average(0) | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_curr_price_level: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.future_prices_avg | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        future_prices_16h: >-
          {% if (this.attributes.future_prices | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices | default([]) | list)[0:16]}}
          {% endif %}
        future_prices_16h_totals: >-
          {% if (this.attributes.future_prices_16h | default('unknown')) in ['unknown','unavailable','none'] %}
            unknown
          {% else %}
            {{ (this.attributes.future_prices_16h | default([])) | map(attribute='total') | list }}
          {% endif %}
        #future_prices_16h_count: "{{ (this.attributes.future_prices_16h_totals | default([])) | count }}"
        future_prices_16h_min: |-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | min | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_max: |-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | max | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_avg: >-
          {% if this.attributes.future_prices_16h_totals | default([0]) | count > 0 %}
            {{ (this.attributes.future_prices_16h_totals | default([0])) | average(0) | float(0) | round(4) }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_current_price_level: >-
          {% set price_cur = this.state | default(0) | float(0) %}
          {% set price_avg = this.attributes.future_prices_16h_avg | default(0) | float(0) %}
          {% if price_cur == 0 or price_avg == 0 %}
            unknown
          {% else %}
            {% set price_ratio = (price_cur / price_avg) %}
            {% if price_ratio >= 1.4 %}
              Very expensive
            {% elif price_ratio >= 1.15 %}
              Expensive
            {% elif price_ratio <= 0.6 %}
              Very cheap
            {% elif price_ratio <= 0.9 %}
              Cheap
            {% else %}
              Normal
            {% endif %}
          {% endif %}
        future_prices_16h_current_price_is_close_to_min: >-
          {% if is_number(this.state) and is_number(this.attributes.future_prices_16h_min) %}
            {{ (this.state | float(0) <= (1.15 * this.attributes.future_prices_16h_min | default(0) | float(0))) | lower }}
          {% else %}
            unknown
          {% endif %}
        future_prices_16h_current_price_is_at_min: >-
          {% if this.state | default(0) | float(0) > 0 and this.attributes.future_prices_16h_min | default(0) | float(0) > 0 %}
            {{ (this.state | default(0) | float(0) <= this.attributes.future_prices_16h_min | default(0) | float(0)) | lower }}
          {% else %}
            unknown
          {% endif %}

The error I am getting in the log is:

Logger: homeassistant.components.sensor
Source: helpers/entity_platform.py:509
Integration: Sensor (documentation, issues)
First occurred: 22:53:18 (1 occurrences)
Last logged: 22:53:18

Error adding entities for domain sensor with platform template
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 616, in state
    numerical_value = int(value)
                      ^^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'unknown'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 619, in state
    numerical_value = float(value)
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: 'unknown'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 509, in async_add_entities
    await asyncio.gather(*tasks)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 754, in _async_add_entity
    await entity.add_to_platform_finish()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1024, in add_to_platform_finish
    await self.async_added_to_hass()
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 537, in async_added_to_hass
    async_at_start(self.hass, self._async_template_startup)
  File "/usr/src/homeassistant/homeassistant/helpers/start.py", line 67, in async_at_start
    return _async_at_core_state(
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/start.py", line 32, in _async_at_core_state
    hass.async_run_hass_job(at_start_job, hass)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 478, in _async_template_startup
    result_info.async_refresh()
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1035, in async_refresh
    self._refresh(None)
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1215, in _refresh
    self.hass.async_run_hass_job(self._job, event, updates)
  File "/usr/src/homeassistant/homeassistant/core.py", line 671, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/components/template/template_entity.py", line 429, in _handle_results
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 745, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 845, in _async_write_ha_state
    state, attr = self._async_generate_attributes()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 786, in _async_generate_attributes
    state = self._stringify_state(available)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 751, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 623, in state
    raise ValueError(
ValueError: Sensor sensor.electricity_price has device class 'None', state class 'None' unit '€/kWh' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: 'unknown' (<class 'str'>)
redlefloh commented 9 months ago

@edwardtfn would you mind taking a look, thank you!

edwardtfn commented 9 months ago

My apologies, I've missed your previous message. I will take a look and come back to you. It's nice to look at that code anyways.., if i was writting that today it probably would be a bit different, 😂

edwardtfn commented 9 months ago

Anyways, I was looking my sensor now (which is working fine) and I saw it is slightly different than yours (maybe the version on GitHub was outdates, I've just pushed the latest).

Could you please try this and let me know if you still having the same issue?

template:
  - sensor:
      - name: Electricity price
        unique_id: d4c04abf-c258-4f0f-a61d-cc0170225081
        unit_of_measurement: "SEK/kWh"
        icon: mdi:currency-usd
        device_class: monetary
        availability: "{{ state_attr('sensor.tibber_electricity_price', 'today') | default('unknown') not in ['unknown','unavailable', None] }}"
        state: "{{ this.attributes.current.total | float(0) if this.attributes is defined and this.attributes.current is defined and this.attributes.current.total is defined and is_number(this.attributes.current.total) else None }}"
        attributes:
          all_prices: "{{ state_attr('sensor.tibber_electricity_price', 'today') | default([]) + state_attr('sensor.tibber_electricity_price', 'tomorrow') | default([]) }}"
          min_price: "{{ this.attributes.all_prices | map(attribute='total') | list | min if this.attributes.all_prices is defined and this.attributes.all_prices | count > 0 else None }}"
          max_price: "{{ this.attributes.all_prices | map(attribute='total') | list | max if this.attributes.all_prices is defined and this.attributes.all_prices | count > 0 else None }}"
          avg_price: "{{ this.attributes.all_prices | map(attribute='total') | list | average(0) if this.attributes.all_prices is defined and this.attributes.all_prices | count > 1 else this.attributes.max_price | default(None) }}"
          future: "{{ (this.attributes.all_prices | default([])) | selectattr('startsAt', 'gt', (now() + timedelta(hours=-1)).isoformat()) | list if this.attributes.all_prices is defined else [] }}"
          future_min_price: "{{ this.attributes.future | map(attribute='total') | list | min if this.attributes.future is defined and this.attributes.future | count > 0 else None }}"
          future_max_price: "{{ this.attributes.future | map(attribute='total') | list | max if this.attributes.future is defined and this.attributes.future | count > 0 else None }}"
          future_avg_price: "{{ this.attributes.future | map(attribute='total') | list | average(0) if this.attributes.future is defined and this.attributes.future | count > 1 else this.attributes.future_max_price | default(None) }}"
          current: "{{ this.attributes.future | first if this.attributes.future | count > 0 else None }}"
          price_level: "{{ this.attributes.current.level | default('unknown') | replace('_', ' ') | capitalize if this.attributes.current is defined and this.attributes.current.level is defined else None }}"
          price_level_1d: >-
            {% set price_cur = this.state | default(0) | float(0) %}
            {% set price_avg = this.attributes.avg_price | default(0) | float(0) %}
            {% if price_cur == 0 or price_avg == 0 %}
              unknown
            {% else %}
              {% set price_ratio = (price_cur / price_avg) %}
              {% if price_ratio >= 1.4 %}
                Very expensive
              {% elif price_ratio >= 1.15 %}
                Expensive
              {% elif price_ratio <= 0.6 %}
                Very cheap
              {% elif price_ratio <= 0.9 %}
                Cheap
              {% else %}
                Normal
              {% endif %}
            {% endif %}
          price_level_combined: >-
            {% set level1 = this.attributes.price_level_1d | default('unknown') %}
            {% set level3 = this.attributes.price_level | default('unknown') %}
            {% if level1 == level3 %}
              {{ level1 }}
            {% elif level1 in ['unknown','unavailable','none'] or level3 in ['unknown','unavailable','none'] %}
              unknown
            {% elif level1 == "Very cheap" %}
              {{ level3 }}
            {% elif level3 == "Very cheap" %}
              {{ level1 }}
            {% elif level1 == "Cheap" %}
              {{ level3 }}
            {% elif level3 == "Cheap" %}
              {{ level1 }}
            {% elif level1 == "Normal" %}
              {{ level3 }}
            {% elif level3 == "Normal" %}
              {{ level1 }}
            {% elif level1 == "Expensive" %}
              {{ level3 }}
            {% else %}
              {{ level1 }}
            {% endif %}
          is_below_average: "{{ (this.state | float(0) < this.attributes.avg_price | float(0)) if is_number(this.state) and is_number(this.attributes.avg_price) else None }}"
          is_above_average: "{{ (this.state | float(0) > this.attributes.avg_price | float(0)) if is_number(this.state) and is_number(this.attributes.avg_price) else None }}"
          is_at_min: "{{ (this.state | float(0) <= this.attributes.min_price | float(0)) if is_number(this.state) and is_number(this.attributes.min_price) else None }}"
          is_at_max: "{{ (this.state | float(0) >= this.attributes.max_price | float(0)) if is_number(this.state) and is_number(this.attributes.max_price) else None }}"
          is_close_to_min: "{{ (this.state | float(0) <= (1.15 * this.attributes.min_price | float(0))) if is_number(this.state) and is_number(this.attributes.min_price) else None }}"
          future_price_level: >-
            {% set price_cur = this.state | default(0) | float(0) %}
            {% set price_avg = this.attributes.future_avg_price | default(0) | float(0) %}
            {% if price_cur == 0 or price_avg == 0 %}
              unknown
            {% else %}
              {% set price_ratio = (price_cur / price_avg) %}
              {% if price_ratio >= 1.4 %}
                Very expensive
              {% elif price_ratio >= 1.15 %}
                Expensive
              {% elif price_ratio <= 0.6 %}
                Very cheap
              {% elif price_ratio <= 0.9 %}
                Cheap
              {% else %}
                Normal
              {% endif %}
            {% endif %}
          next_16h: "{{ (this.attributes.future | default([])) | selectattr('startsAt', 'lt', (now() + timedelta(hours=16)).isoformat()) | list if this.attributes.future is defined else [] }}"
          next_16h_min_price: "{{ this.attributes.next_16h | map(attribute='total') | list | min if this.attributes.next_16h is defined and this.attributes.next_16h | count > 0 else None }}"
          next_16h_is_at_min: "{{ (this.state | float(0) <= this.attributes.next_16h_min_price | float(0)) if is_number(this.state) and is_number(this.attributes.next_16h_min_price) else None }}"

And is the sensor sensor.tibber_electricity_price working properly for you? It is required for this one.

redlefloh commented 8 months ago

Hi @edwardtfn, sorry for the holiday delay... hope you enjoyed the holidays! The new version works just fine, thanks so much!

Screenshot 2023-12-28 at 01 11 37