Extraire des data d'une requête SQL (sql sensor integration) simplement

Bonsoir à tous,

C’est un problème sans être vraiment un problème, car cela marche, mais cela ressemble à un bazouka pour tuer une mouche …

Mon objectif global, pour avoir le contexte, est de créer un sensor via template pour définir l’état probable du portail automatique si je n’ai pas un état franc ( pas fermé et pas ouvert). Pour ce faire, j’ai un ensemble de conditions en fonction des derniers états validés connus (via Template jinja simple) et de la dernière séquence de commandes envoyés (sensor sql).

Ma requête sql est aussi plutôt simple: je recherche les 6 dernières commandes d’état « ON » (je ne prends pas le off , car juste le contact sec m’intéresse) pour les switchs switch.plusuniportail_switch_0 (contact no pour ouverture/fermeture piéton) et switch.plusuniportail_switch_1 (contact no pour ouverture/fermeture voiture) rien de problématique encore à ce niveau, ma requête fonctionne à merveille (je vous conseille d’ailleur DB Brower for SQlite qui est très pratique pour les tests)

La ou je trouve cela compliqué, c’est la prise de tête pour sortir des data exploitable du résultat de requête en pseudo json via le jinja… Je suis obligé de concaténé chaque colonne en espèce de json stringifier (et pas l’ensemble pour ne pas dépasser les 255 car) pour ensuite traduire cela en jinja en sachant que le attribut_A[i] va avec le attribut_B[i], etc … :face_with_head_bandage:

On ne pas pas faire plus simple ???

Requête sql de base :

SELECT
        datetime(julianday(states.last_updated_ts , 'unixepoch')) as last_updated_ts,
        CAST(round((julianday(datetime('now')) - julianday(states.last_updated_ts , 'unixepoch'))*24*60*60) AS INTEGER) as since_sec,    
        CASE WHEN states_meta.entity_id   = 'switch.plusuniportail_switch_0'   THEN 'partial' ELSE 'complet' END AS sent_command,
        states_meta.entity_id       
      FROM
        states
      LEFT JOIN states_meta ON states.metadata_id = states_meta.metadata_id    
      WHERE
        states.state = 'on' 
        AND
        (
            states.metadata_id = (
              SELECT
                metadata_id
              FROM
                states_meta
              WHERE
                entity_id = 'switch.plusuniportail_switch_0' 
              )
        OR
        states.metadata_id = (
              SELECT
                metadata_id
              FROM
                states_meta
              WHERE
                entity_id = 'switch.plusuniportail_switch_1' 
              )
          )
      ORDER BY
        last_updated_ts DESC
      LIMIT
      6

Résultat normal, mais malheureusement inexploitable dans Home Assistant :

last_updated_ts since_sec sent_command entity_id
2025-03-05 19:44:15 255041 partial switch.plusuniportail_switch_0
2025-03-05 18:53:22 258094 complet switch.plusuniportail_switch_1
2025-03-05 18:52:52 258123 complet switch.plusuniportail_switch_1
2025-03-05 18:52:47 258128 complet switch.plusuniportail_switch_1
2025-03-05 18:41:20 258815 complet switch.plusuniportail_switch_1
2025-03-05 18:40:45 258851 complet switch.plusuniportail_switch_1

Requête sql avancée pour concaténer les résultats en pseudo tableau json:

SELECT  
        sum (grp) as count,
        '{"data":["' || group_concat(last_updated_ts,'","') || '"]}' as last_updated_ts,
        '{"data":["' || group_concat(since_sec,',') || '"]}"' as since_sec,
        '{"data":["' || group_concat(sent_command,'","') || '"]}' as sent_command,
        '{"data":["' || group_concat(entity_id,'","') || '"]}' as entity_id
        
      FROM
      (
        SELECT
          1 as grp,
              datetime(julianday(states.last_updated_ts , 'unixepoch')) as last_updated_ts,
              CAST(round((julianday(datetime('now')) - julianday(states.last_updated_ts , 'unixepoch'))*24*60*60) AS INTEGER) as since_sec,    
              CASE WHEN states_meta.entity_id   = 'switch.plusuniportail_switch_0'   THEN 'partial' ELSE 'complet' END AS sent_command,
              states_meta.entity_id,
            FROM
              states
            LEFT JOIN states_meta ON states.metadata_id = states_meta.metadata_id    
            WHERE
              states.state = 'on' 
              AND
              (
                  states.metadata_id = (
                    SELECT
                      metadata_id
                    FROM
                      states_meta
                    WHERE
                      entity_id = 'switch.plusuniportail_switch_0' 
                    )
              OR
              states.metadata_id = (
                    SELECT
                      metadata_id
                    FROM
                      states_meta
                    WHERE
                      entity_id = 'switch.plusuniportail_switch_1' 
                    )
                )
            ORDER BY
              last_updated_ts DESC
            LIMIT
            6
      )

SQL avancée résulta :

count last_updated_ts since_sec sent_command entity_id
6 {« data »:[« 2025-03-05 19:44:15 »,…]} {« data »:[253799,…]} {« data »:[« partial »,« complet »,…]} {« data »:[« switch.plusuniportail_switch_0 »,…]}

Soit mon capteur sql sensor :

sql:
  - name: portail_ShellyCmd_SQL_history
    query: >
       (la requête vient ici , je vous en fait grace ;) )
    column: count

et finalement le template jinja pour accéder au données (l’une des données, la 3ieme entrée pour l’exemple)

{{ (state_attr('sensor.portail_shellycmd_sql_history', 'last_updated_ts') | from_json ).data[2]}}
{{ (state_attr('sensor.portail_shellycmd_sql_history', 'since_sec') | from_json ).data[2]}}
{{ (state_attr('sensor.portail_shellycmd_sql_history', 'sent_command') | from_json ).data[2]}}
{{ (state_attr('sensor.portail_shellycmd_sql_history', 'entity_id') | from_json ).data[2]}}

:scream: :exploding_head:

Je suis preneur de toute critique et optimisation !
Par avance merci

Tu as également les fonctions Json de SQLite qui peuvent simplifier les requêtes : JSON Functions And Operators

Bonsoir,
Merci :slight_smile:

C’est ce que j’avais au départ, mais le problème est la récupération dans HA. Car si je fait un json_object par ligne, je ne peut récupérer que la première dans HA je n’arrive pas à accéder au autre. et si je fais un json_object global de chaque ligne, il dépasse les 255 caractères max d’un attribut. alors qu’en faisant un json d’une colonne dans un attribut j’arrive à passer par la seul ligne que récupère HA avec des json pas trop long.

Après il existe peut-être un moyen de récupérer dans le sensor le résultat et d’accéder à chaque row, mais c’est plutôt mal documenté et de tout ce que j’ai trouvé, les requêtes on une limite de 1 pour justement n’avoir qu’une ligne.

Voici la requête initiale que je n’arrivais pas à exploiter ds HA car trop longue :

      SELECT
        json_group_array(
          json_object(
            'last_updated_ts', datetime(julianday(states.last_updated_ts , 'unixepoch')),
            'since_sec', CAST(round((julianday(datetime('now')) - julianday(states.last_updated_ts , 'unixepoch'))*24*60*60) AS INTEGER),    
            'sent_command',CASE WHEN states_meta.entity_id   = 'switch.plusuniportail_switch_0'   THEN 'partial' ELSE 'complet' END,
            'entity_id',states_meta.entity_id
          ) 
        ) as json_data
      FROM
        states
      LEFT JOIN states_meta ON states.metadata_id = states_meta.metadata_id    
      WHERE
        states.state = 'on' 
        AND
		  ( states.metadata_id = ( SELECT metadata_id FROM states_meta WHERE entity_id = 'switch.plusuniportail_switch_0' )
			OR
			states.metadata_id = ( SELECT metadata_id FROM states_meta WHERE entity_id = 'switch.plusuniportail_switch_1' )
          )
      ORDER BYlast_updated_ts DESC
      LIMIT 6

C’est malheureusement comme ça…l’intégration SQL ne fourni que un résultat.
SQL - Home Assistant

Bonsoir,

Bon et bien faute de mieux on va conserver cela …
En tout cas, voici ma petite macro pour reconcaténer toutes les data en objet par ligne et par champs pour un usage sur un sensor identique à celui décrit ci-dessus.

{#  MACRO building a strignify object of the sql result  #}

{#  SQL result need to return a single with a strignify json in each reults merged in each column #}

{#  usage :  #}
{#       prerequis, le sensor sql doit avoir comme state le nombre de lignes réelles contenu dans la 1er colonnes count #}
{#      {% from 'sql_to_dictonnary.jinja' import sql_to_dictonnaries_Array %}                   #}
{#      {% set sqlSensor = 'sensor.mySensorName' -%}                           #}
{#      {%- set sqlFields = ['sqlResultField1','sqlResultField2',...] -%}      #}
{#      {%- set data = sql_to_dictonnaries_Array(sqlSensor, sqlFields) | from_json  -%}         #}
{#      {{ (data)[i].sqlResultField1}}                                                               #}

{#      voir exemple requete + utilisation : /config/_yamls/_packages/package_portail.yaml            #}

{%- macro sql_to_dictonnaries_Array(sqlSensor,sqlFields) -%}
  {%- set count = states(sqlSensor) | int -%}
  {%- set ns = namespace(jsonString='[{') -%} {# namespace() permet de declarer une variable sans "trop de" problème de scope #}
  {%- for i in range(count) -%}    
    {# loop through fields to build stringify json object #}
      {%- for attr in sqlFields -%}
        {# prepare val and format with  quote if string #}
          {%- set val = (state_attr(sqlSensor, attr) | from_json).data[i] -%}
          {%- set formattedVal = '"'+val+'"' if  ((val|int(-1) == -1) or (val|bool(-1) == -1)) else (val|string) -%}
        {# write pair  key value in stringify json object#}
          {%- set ns.jsonString = ns.jsonString + '"'+attr+'":'+formattedVal -%}
        {# if it's not the last we add comma#}
          {%- if loop.last != True -%}
           {%- set ns.jsonString = ns.jsonString + ',' -%}
          {%- endif -%}        
      {%- endfor -%}
    {#--#}
    {# close object or add coma to continu in the next llop#}
     {%- if loop.last != True -%}     
        {%- set ns.jsonString = ns.jsonString + '},{' -%}
      {%- else -%} 
       {%- set ns.jsonString = ns.jsonString + '}]' -%}
      {%- endif -%}
  {%- endfor -%} 
  {# return stringify json to object#}
  {{- ns.jsonString -}}
{%- endmacro -%}