Erreur "numerical_value = float(value)"

Hello,

Depuis qqes temps (une maj ?), j’ai des erreurs récurrentes dans mes logs :

2023-08-02 17:43:24.601 ERROR (MainThread) [homeassistant.helpers.event] Error while dispatching event for sensor.tesla_wall_connector_energy to <Job track state_changed event {'sensor.tesla_wall_connector_energy'} HassJobType.Callback <bound method TrackTemplateResultInfo._refresh of <TrackTemplateResultInfo {Template<template=({% set energieA = states('sensor.tesla_wall_connector_energy') %} {% if energieA == 'unavailable'%}unavailable{% else %}
  {{ ((energieA | float) / 1000) | round(2, default=0) }}
{% endif %}) renders=3384>: <RenderInfo Template<template=({% set energieA = states('sensor.tesla_wall_connector_energy') %} {% if energieA == 'unavailable'%}unavailable{% else %}
  {{ ((energieA | float) / 1000) | round(2, default=0) }}
{% endif %}) renders=3384> all_states=False all_states_lifecycle=False domains=frozenset() domains_lifecycle=frozenset() entities=frozenset({'sensor.tesla_wall_connector_energy'}) rate_limit=None has_time=False exception=None is_static=False>}>>>
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 592, in state
    numerical_value = float(value)  # type:ignore[arg-type]
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: 'unavailable'

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

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 274, in _async_dispatch_entity_id_event
    hass.async_run_hass_job(job, event)
  File "/usr/src/homeassistant/homeassistant/core.py", line 627, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 1156, in _refresh
    self.hass.async_run_hass_job(self._job, event, updates)
  File "/usr/src/homeassistant/homeassistant/core.py", line 627, in async_run_hass_job
    hassjob.target(*args)
  File "/usr/src/homeassistant/homeassistant/helpers/template_entity.py", line 362, in _handle_results
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 742, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 810, in _async_write_ha_state
    state = self._stringify_state(available)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 748, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 594, in state
    raise ValueError(
ValueError: Sensor sensor.total_energie_twc has device class 'energy', state class 'total_increasing' unit 'kWh' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: 'unavailable' (<class 'str'>)

Ca se produit quand dans un de mes templates, j’ai une valeur ‹ undefined › ou ‹ unavailable › comme état d’un sensor.
Exemple de template en question:

    - name: "Total énergie TWC"
      unique_id: total_energie_TWC
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing
      state: >
        {% set energieA = states('sensor.tesla_wall_connector_energy') %}
        {% if energieA == 'unavailable'%}unavailable{% else %}
          {{ ((energieA | float) / 1000) | round(2, default=0) }}
        {% endif %}

Pourtant avant de faire un calcul, je check si c’est pas ‹ unavailable › et si c’est le cas je mets le state du sensor à ‹ unavailable ›.

Ca marchait bien avant. Si quelqu’un a une idée du pourquoi et de comment il faut faire pour éviter ces erreurs, je prends.

Attention, je ne veux pas mettre ma valeur à 0 si c’est indisponible sinon ca va me flinguer ma valeur d’énergie qui est en total_increasing

remarque: j’ai bien vu ce post là (Erreur template 2022.1 - #4 par Pulpy-Luke) mais ça répond pas à la question.

Merci d’avance.

Salut

En fait si, ça réponds. La valeur par défaut de float() EST OBLIGATOIRE désormais (comme int() et round() etc).
Par contre, comme tu le fais le test du unavailable avant, ça n’aura aucune incidence dans ton total_incursing…
Et puis même ajouter 0 à un total, ça fait pas beaucoup de différence mathématiquement parlant

Merci @Pulpy-Luke pour ta répondre super rapide.

J’ai pas été clair du coup, c’est ma faute. Le sensor de base sensor.tesla_wall_connector_energy est déjà un total_increasing et je veux juste le mettre en kWh au lieu de Wh :

Si je renvoie 0, je vais passer de 4843 kWh à 0 kWh et ça va me flinguer mon dashboard énergie.

Comme je teste avant, je pense que mon problème n’est pas dans la ligne:
{{ ((energieA | float) / 1000) | round(2, default=0) }}
mais plutot ici: {% if energieA == 'unavailable'%}unavailable
J’ai l’impression que HA n’aime pas que je set mon état à unavailable. C’est lui tente de le convertir en float et qui me fait cette erreur.

En fait, c’est pas ça ton souci mais bien ton float

{{ ((energieA | float) / 1000) | round(2, default=0) }}

DOIT DEVENIR

{{ ((energieA | float(default=0)) / 1000) | round(2, default=0) }}

Et comme dit plus haut, aucun impact sur le total_increasing… Soit tu as une valeur, et tu passes ton test (et donc la valeur par défaut ne sert à rien) , soit tu n’as pas de valeur, le test echoue et tu ne fera pas le calcul.
Par contre HA ATTENDS d’avoir une valeur par défaut quand il se charge (point de vue de la syntaxe), et il ne sait pas à l’avance si la valeur existera ou pas

Je vais essayer, ca mange pas beaucoup de pain puisque je teste avant. Le defaut dans le float ne devrait servir à rien.

Merci @Pulpy-Luke

Là ou tu as raison c’est qu’il faut peut-être aussi gérer le unavailable ailleurs que dans le test

Tu vois ça fait pareil (sur un autre calcul pareil que j’ai modifié):

    - name: "Total puissance radiateur entree"
      unique_id: total_power_radiateur_entree
      device_class: power
      unit_of_measurement: "kW"
      state_class: measurement
      state: >
        {% if is_state('sensor.radiateur_entree_power', 'unavailable') %}unavailable{% else %}
          {{ ((states('sensor.radiateur_entree_power') | float(default=0)) / 1000) | round(2, default=0) }}
        {% endif %}
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 748, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 594, in state
    raise ValueError(
ValueError: Sensor sensor.total_puissance_radiateur_entree has device class 'power', state class 'measurement' unit 'kW' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: 'unavailable' (<class 'str'>)

Je pense qu’il n’aime pas que retourne ‹ unavailable › comme état. Je vais aller voir le code.

Je ne sais pas s’il y a ça dispo dans les totaux : availability

EDIT : il faut ‹ none › !!

state template REQUIRED

Defines a template to get the state of the sensor. If the sensor is numeric, i.e. it has a state_class or a unit_of_measurement, the state template must render to a number or to none. The state template must not render to a string, including unknown or unavailable. An availability template may be defined to suppress rendering of the state template.

donc pour être tranquille

    - name: "Total énergie TWC"
      unique_id: total_energie_TWC
      unit_of_measurement: "kWh"
      device_class: energy
      state_class: total_increasing
      state: >
        {% set energieA = states('sensor.tesla_wall_connector_energy') %}
        {% if energieA == 'unavailable'%}none{% else %}
          {{ ((energieA | float(default=0)) / 1000) | round(2, default=0) }}
        {% endif %}
1 « J'aime »

Bon ben c’est ça quand on regarde le code de sensor, il y a des device_class (power dans mon cas), qui sont forcément numériques et on ne peut plus mettre ‹ unavailable › dedans, en tout cas pas par un template.

sensor.init.py:

if not self._numeric_state_expected:
            return value

        # From here on a numerical value is expected
        numerical_value: int | float | Decimal
        if not isinstance(value, (int, float, Decimal)):
            try:
                if isinstance(value, str) and "." not in value and "e" not in value:
                    numerical_value = int(value)
                else:
                    numerical_value = float(value)  # type:ignore[arg-type]
            except (TypeError, ValueError) as err:
                raise ValueError(
                    f"Sensor {self.entity_id} has device class '{device_class}', "
                    f"state class '{state_class}' unit '{unit_of_measurement}' and "
                    f"suggested precision '{suggested_precision}' thus indicating it "
                    f"has a numeric value; however, it has the non-numeric value: "
                    f"'{value}' ({type(value)})"

et:

def _numeric_state_expected(self) -> bool:
        """Return true if the sensor must be numeric."""
        # Note: the order of the checks needs to be kept aligned
        # with the checks in `state` property.
        device_class = try_parse_enum(SensorDeviceClass, self.device_class)
        if device_class in NON_NUMERIC_DEVICE_CLASSES:
            return False
        if (
            self.state_class is not None
            or self.native_unit_of_measurement is not None
            or self.suggested_display_precision is not None
        ):
            return True

et :

NON_NUMERIC_DEVICE_CLASSES = {
    SensorDeviceClass.DATE,
    SensorDeviceClass.ENUM,
    SensorDeviceClass.TIMESTAMP,
}

Ce qui m’étonne c’est que je sois le seul dans ce cas là …

Regarde l’edit de mon message précédent :wink:

Bien joué. Je l’ai loupé.

Y a aussi un ‹ availability › template attribut qu’on peut utiliser pour dire si le template doit être valorisé:

availability [template](https://www.home-assistant.io/docs/configuration/templating/) (optional, default: true)

Defines a template to get the `available` state of the entity. If the template either fails to render or returns `True`, `"1"`, `"true"`, `"yes"`, `"on"`, `"enable"`, or a non-zero number, the entity will be `available`. If the template returns any other value, the entity will be `unavailable`. If not configured, the entity will always be `available`. Note that the string comparison is not case sensitive; `"TrUe"` and `"yEs"` are allowed.```

Grrrr:

    numerical_value = float(value)  # type:ignore[arg-type]
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: 'none'```

None avec un N majuscule ? Avec ou sans quotes ?

La bonne syntaxe (ça s’invente pas) : {{none}}
Si on met none, ca rend la chaine ‹ none ›.

    - name: "Total puissance radiateur entree"
      unique_id: total_power_radiateur_entree
      device_class: power
      unit_of_measurement: "kW"
      state_class: measurement
      state: >
        {% if is_state('sensor.radiateur_entree_power', 'unavailable') %}{{none}}{% else %}
          {{ ((states('sensor.radiateur_entree_power') | float(default=0)) / 1000) | round(2, default=0) }}
        {% endif %}
2 « J'aime »

Une petite amélioration car la valeur peut être aussi unknown et pas que unavailable.

Donc le meilleur template qui va marcher à tous les coups pour faire ça c’est celui-là:

{% set power = states('sensor.total_puissance_consommee_instantanee_w') | float(default=-1.0) %}
{% if power < 0 %}{{none}}{% else %}
     {{ (power / 1000) | round(2) }}
{% endif %}

Pour info, j’ai testé aussi is number : {% if states(‹ sensor.total_puissance_consommee_instantanee_w ›) is number %} … mais ca ne fonctionne pas car states(xxxx) est toujours une string. Donc obligé de convertir pour savoir si on a bien un float dans le state.

Bonjour @Jean-Marc_Collin !
Je me retrouve un peu avec le même problème que toi !
J’ai souhaité créer un sensor température de sécurité au cas où les serveurs de Météo France plantent de nouveau… Dans ce cas, cela me renverrai la température de l’intégration Met.no de base.

{% if states('sensor.chalon_sur_saone_temperature') == 'unavailable' %}
  {{ state_attr('weather.appartement', 'temperature') }}
{% else %}
  {{ states('sensor.chalon_sur_saone_temperature') }}
{% endif %}

J’en ai profité pour faire la même chose avec la température intérieure (si mon capteur du salon n’a plus de batterie, je peux avoir celui de la chambre) :

{% if states('sensor.capteur_temperature_salon_temperature') == 'unavailable' %}
  {{ states('sensor.capteur_temperature_chambre_rdc_temperature') }}
{% else %}
  {{ states('sensor.capteur_temperature_salon_temperature') }}
{% endif %}

Les deux sensors sont des device_class: temperature ce qui doit entrainer des problèmes avec le unavailable.
J’ai ce genre de problème dans les logs :

Entity sensor.temperature_exterieure_securite (<class 'homeassistant.components.template.sensor.SensorTemplate'>) is using native unit of measurement 'None' which is not a valid unit for the device class ('temperature') it is using; expected one of ['K', '°C', '°F']; Please update your configuration if your entity is manually configured, otherwise create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+template%22

J’ai eu également des messages d’erreurs dans le log, comme sur ton premier post.
Est-ce que je pourrai modifier quelque chose pour éviter cela ?

C’est le ‹ unit_of_measurement › qui doit manqué dans ton template. Regarde le template au-dessus qui s’apelle total_power_radiateur_entree. Il faut définir un unit_of_measurement , un device_class, et un state_class.

1 « J'aime »