Graphiques groupés ET empilés

Bonjour,

Je poursuis avec plus ou moins de bonheur mon parcours initiatique sur Jeedom.

J’explore les Vues et les Designs et je trouve surprenant que les vues qu’on s’amuse à fignoler ne soit pas intégrables directement dans les Design mais qu’on doive récréer des « Graphiques » qui reprennent sensiblement les mêmes infos et les mêmes menus (sauf toutefois la possibilité d’ordonner les séries).

C’est peut-être historique et c’est peut-être aussi une piste d’amélioration de fusionner ces deux fonctionnalités.

Par ailleurs, j’ai cherché sans succès à obtenir des graphiques (colonne ou pas) à la fois groupés et empilés comme on peut le faire avec Excel par exemple. Je n’ai pas trouvé comment faire et je ne suis pas sûr que ce soit possible.

Quelqu’un a-t-il un retour d’expérience là-dessus ?

Merci et bonne journée à tous.

Bonjour,

Si c’est avoir pour ce genre de graphique empilés :

image

C’est faisable.
Il suffit de configurer les graphiques comme suit :

Bonjour,

Hélas non… ça je sais le faire. Ce que je voudrais faire, c’est ça :

2x2 séries empilées

Ah, OK…
En 3x1, ça fonctionne en effet (il suffit de décocher la case Empiler de l’une des valeurs), mais en 2x2 je ne sais pas…

1 « J'aime »

Merci… A suivre donc…

1 « J'aime »

Bonjour,

Si vous voulez faire des courbes dans tous les sens paramétrable et autres, ce n’est pas trop le but de Jeedom.

Faut utiliser Grafana

Ca semble possible avec highcharts qui est intégré à Jeedom:

Mais ce n’est pas accessible par l’interface. Il faut le programmer.

Merci de ce retour.

Je ne connais pas Grafana mais ça a l’air tres intéressant. Je vois qu’il y a une API. Pensez-vous qu’il soit possible que Jeedom envoie des donnees a Grafana et recupere les visualisation dans un Design ?

Ce serait top!

Je connais pas HighCharts mais ça a aussi l’air vraiment très bien. J’ai l’impression cependant que c’est un produit payant.

Je suis prêt à programmer quelque chose mais les tutos que je trouve (comme celui-ci) se concentrent sur l’accessoire (les couleurs, leurs formes, les css, etc…) en omettant l’essentiel: comment établir la connexion entre HighCharts et Jeedom ?

Je vais farfouiller pour essayer trouver mais si vous avez un lien explicatif, ce serait super.

Merci !

Bon… comme d’habitude, c’est assez cryptique…

J’ai tenté un essai HighCharts en me fondant sur le tuto [PARTAGE] Jauges Highcharts paramétrable, ouvert aux contributions. Ce n’est pas le type de graphique que je souhaite mais c’était pour tester.

On écrit dans le tuto d’aller créer un Widget en mode code

Puis de cliquer sur le bouton « nouveau ». Problème: je n’ai pas de bouton « nouveau » mais je me dis que cela a peut-être changé et que c’est désormais le bouton « créer un widget »

Alors je clique sur « créer un widget » et effectivement j’ai la fenêtre qui va bien qui s’ouvre :

J’indique alors comme dans le tuto que je veux crée un widget de type info et de sous-type numérique et de Nom JaugeHighchart et là… j’ai un message d’erreur: « le sous-type ne peut être vide ».

Il n’est pourtant pas vide.
J’avais par ailleurs bien créer le fichier cmd.info.numeric.JaugeHorizontal.html dans le répertoire qui va bien…

Bref, je suis à nouveau bloqué… Je suis preneur d’une doc « Highcharts dans Jeedom » si elle existe.

Merci !

Bonjour,
La réponse de Jeedom concernant Le sous type ne peut être vide:

Vous avez vu l’exemple pour savoir comment créer les axes et les séries de données pour qu’elles soient groupées ?
On y accède par le bouton image sur la page de l’exemple fourni plus haut.

1 « J'aime »

Ah merci ! Je n’avais pas pensé à aller chercher un lien sur l’erreur… j’ai pas encore les réflexes.

Je vais retenter dès que j’ai 5 minutes.

Le lien que vous partagez ne fonctionne pas (erreur 404) mais j’avais bien vu celui-ci: https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/css/x-range/

A vrai dire, je ne sais pas du tout comment je vais intégrer ces fichiers html, css et js dans jeedom mais je vais tenter de comprendre.

Merci encore !

Effectivement erreur 404, je corrige.
J’y accède en cliquant sur le bouton JS Fiddle sous l’exemple highcharts.

1 « J'aime »

Bon… je progresse mais très lentement en raison de ma méconnaissance de jeedom.

1. Première étape:

Suite au bug signalé par Loïc sur la création de widget code, je l’ai créé à la main et j’y est collé le code Highcharts (html + css + javascript). A ce stade, cela revient à coller le fichier exemple avec des données statiques

Le code:

<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<style>
  .highcharts-figure,
.highcharts-data-table table {
    min-width: 310px;
    max-width: 800px;
    margin: 1em auto;
}

#container {
    height: 400px;
}

.highcharts-data-table table {
    font-family: Verdana, sans-serif;
    border-collapse: collapse;
    border: 1px solid #ebebeb;
    margin: 10px auto;
    text-align: center;
    width: 100%;
    max-width: 500px;
}

.highcharts-data-table caption {
    padding: 1em 0;
    font-size: 1.2em;
    color: #555;
}

.highcharts-data-table th {
    font-weight: 600;
    padding: 0.5em;
}

.highcharts-data-table td,
.highcharts-data-table th,
.highcharts-data-table caption {
    padding: 0.5em;
}

.highcharts-data-table thead tr,
.highcharts-data-table tr:nth-child(even) {
    background: #f8f8f8;
}

.highcharts-data-table tr:hover {
    background: #f1f7ff;
}

.highcharts-description {
    margin: 0.3rem 10px;
}
</style>
<figure class="highcharts-figure">
    <div id="container"></div>
    <p class="highcharts-description">
        Chart showing stacked columns with grouping, allowing specific series to
        be stacked on the same column. Stacking is often used to visualize
        data that accumulates to a sum.
    </p>
</figure>
<script>
  // Data retrieved from https://en.wikipedia.org/wiki/Winter_Olympic_Games
Highcharts.chart('container', {

    chart: {
        type: 'column'
    },

    title: {
        text: 'Olympic Games all-time medal table, grouped by continent',
        align: 'left'
    },

    xAxis: {
        categories: ['Gold', 'Silver', 'Bronze']
    },

    yAxis: {
        allowDecimals: false,
        min: 0,
        title: {
            text: 'Count medals'
        }
    },

    tooltip: {
        format: '<b>{key}</b><br/>{series.name}: {y}<br/>' +
            'Total: {point.stackTotal}'
    },

    plotOptions: {
        column: {
            stacking: 'normal'
        }
    },

    series: [{
        name: 'Norway',
        data: [148, 133, 124],
        stack: 'Europe'
    }, {
        name: 'Germany',
        data: [102, 98, 65],
        stack: 'Europe'
    }, {
        name: 'United States',
        data: [113, 122, 95],
        stack: 'North America'
    }, {
        name: 'Canada',
        data: [77, 72, 80],
        stack: 'North America'
    }]
});

</script>

2. je me doute bien que je vais devoir remplacer ces données statiques par celles issues en dynamique de commandes Jeedom

ChatGPT qui est mon ami (parce que pour trouver l’info autrement… bonjour) m’indique que la syntaxe va être quelque chose de ce genre (modulo des concaténations et tripatouillages divers et variés pour se conformer à la structure qu’attend Highcharts):

var data = jeedom.cmd.get({id: "ton_id_de_commande"});
Highcharts.chart('container', {
  series: [{
    name: 'Données dynamiques',
    data: data // insérer les données récupérées
  }]
});

3. en ensuite ?

je ne sais pas trop parce que le widget code créé à la main n’apparaît pas dans la liste de mes widgets. En revanche, il apparaît dans la liste des possibles remplacements ce qui m’effraie: DanielJ explique en effet que cet outil de remplacement est irréversible (j’aurais envie d’écrire irrémédiable) et comme je suis tout sauf sûr de moi… j’hésite.

Deux questions ouvertes pour moi:

  • Suis-je sur la bonne piste ?
  • Y a-t-il moyen de tester tout cela dans une sandbox sans tout casser ?

ps: j’ai bien vu le plugin script mais ça me paraît encore plus scabreux.

Bonjour,

Je me permets de relancer ce sujet sur les graphique groupés ET empilés parce que - au-delà du fait que je n’ai pour l’instant pas convergé - je lis d’innombrables fils du forum qui m’orientent vers des pistes différentes sans qu’il me soit vraiment possible de comprendre celle(s) qu’il faut employer. Du reste, il y a peut-être plusieurs façon de faire.

Rappel de ce que je veux faire: des histogrammes représentant des valeurs groupées et empilées sur des données historicisés. Une fonctionnalité que propose ici HightCharts

Pour adapter les codes Highcharts, je vois des tas de procédés différents sans que leurs auteurs ne précisent nécessairement ce qu’ils font et où ils le font. De ce que j’ai compris :

Bref, je ne sais pas laquelle (lesquelles) de ces méthodes est la plus appropriée à mon cas. Et pour tout dire, je m’embrouille trop souvent dans les explications détaillées du forum desquelles j’ai du mal à faire ressortir les principes généraux.

Une bonne âme pour m’orienter ?

1 « J'aime »

Bonjour,

Après avoir infiniment pesté, maugréé et vitupéré Jeedom pour ses aspects horriblement geek, je suis heureux de vous présenter enfin une contribution positive sur les graphiques groupés ET empilés:

ça marche ! Miracle, Hosanna…

J’ai donc employé les graphiques HighCharts en me fondant sur l’utilisation du plugin plugin-htmldisplay . Inutile de vous dire que je n’y serai jamais parvenu sans l’aide massive de notre nouvel ami à tous, ChatGPT qui a très largement contribué au code final que voici :

<!DOCTYPE html>
<html lang="fr">
  <head>
    <meta charset="“UTF-8”" />
  </head>
  <body>
    <div style="text-align:center; margin-bottom: 20px;">
  <label for="dateEnd">Date de fin :</label>
  <input type="date" id="dateEnd">

  <label for="duration">Durée (jours) :</label>
  <input type="number" id="duration" min="1" value="7">

  <button onclick="chargerDonnees()">Mettre à jour</button>
</div>


<style>

.highcharts-figure,
.highcharts-data-table table {
    min-width: 310px;
    max-width: 800px;
    margin: 1em auto;
}

#container {
    height: 400px;
}

.highcharts-data-table table {
    font-family: Verdana, sans-serif;
    border-collapse: collapse;
    border: 1px solid var(--highcharts-neutral-color-10, #e6e6e6);
    margin: 10px auto;
    text-align: center;
    width: 100%;
    max-width: 500px;
}

.highcharts-data-table caption {
    padding: 1em 0;
    font-size: 1.2em;
    color: var(--highcharts-neutral-color-60, #666);
}

.highcharts-data-table th {
    font-weight: 600;
    padding: 0.5em;
}

.highcharts-data-table td,
.highcharts-data-table th,
.highcharts-data-table caption {
    padding: 0.5em;
}

.highcharts-data-table thead tr,
.highcharts-data-table tbody tr:nth-child(even) {
    background: var(--highcharts-neutral-color-3, #f7f7f7);
}

.highcharts-description {
    margin: 0.3rem 10px;
}
</style>

<figure class="highcharts-figure">
    <div id="container"></div>
    <p class="highcharts-description">
    </p>
         

    <script>
function getYesterdayDate() {
  const d = new Date();
  d.setDate(d.getDate() - 1);
  return d.toISOString().split('T')[0]; // Format YYYY-MM-DD
}

document.addEventListener("DOMContentLoaded", () => {
  // Valeurs par défaut : hier et 7 jours
  document.getElementById("dateEnd").value = getYesterdayDate();
  chargerDonnees();
});

function chargerDonnees() {
  const dateEnd = document.getElementById("dateEnd").value;
  const duration = parseInt(document.getElementById("duration").value, 10);

  if (!dateEnd || isNaN(duration)) {
    alert("Veuillez saisir une date et une durée valides.");
    return;
  }

  const endDate = new Date(dateEnd);
  const startDate = new Date(endDate);
  startDate.setDate(endDate.getDate() - duration + 1); // inclusif

  const dateStartStr = startDate.toISOString().split('T')[0];
  const dateEndStr = endDate.toISOString().split('T')[0];

  // Réinitialisation
  var timestamps = [], prod_jour = [], cons_jour = [], export_jour = [], import_jour = [];
  var chargements = 0;

  function essayerAfficherGraphique() {
    if (chargements < 4) return;

    const prod_series = timestamps.map((ts, i) => [ts, prod_jour[i]]);
    const cons_series = timestamps.map((ts, i) => [ts, cons_jour[i]]);
    const export_series = timestamps.map((ts, i) => [ts, export_jour[i]]);
    const import_series = timestamps.map((ts, i) => [ts, import_jour[i]]);

    Highcharts.setOptions({
      lang: {
        months: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
          'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
        weekdays: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
        shortMonths: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',
          'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.']
      }
    });

    Highcharts.chart('container', {
      chart: { type: 'column' },
      title: { text: 'Production et consommation électrique', align: 'left' },
      xAxis: {
        type: 'datetime',
        labels: { format: '{value:%e %b}' }
      },
      yAxis: {
        min: 0,
        title: { text: 'Énergie (kWh)' },
        stackLabels: { enabled: true }
      },
      tooltip: {
        xDateFormat: '%A %e %B %Y',
        shared: true
      },
      plotOptions: {
        column: { stacking: 'normal' }
      },
      series: [{
  name: 'Production',
  data: prod_series,
  stack: 'In',
  color: '#00aa00' // vert
}, {
  name: 'Importation',
  data: import_series,
  stack: 'In',
  color: '#ff0000' // rouge vif
}, {
  name: 'Consommation',
  data: cons_series,
  stack: 'Out',
  color: '#0000ff' // bleu
}, {
  name: 'Exportation',
  data: export_series,
  stack: 'Out',
  color: '#800080' // violet
}]
    });
  }

  // Chargement production
  jeedom.history.get({
    cmd_id: 42,
    dateStart: dateStartStr,
    dateEnd: dateEndStr,
    success: function (result) {
      for (let i = 0; i < result.data.length; i++) {
        const d = new Date(result.data[i][0]);
        timestamps.push(d.getTime());
        prod_jour.push(Math.floor(result.data[i][1] / 1000));
      }
      chargements++;
      essayerAfficherGraphique();
    }
  });

  // Chargement consommation
  jeedom.history.get({
    cmd_id: 50,
    dateStart: dateStartStr,
    dateEnd: dateEndStr,
    success: function (result) {
      for (let i = 0; i < result.data.length; i++) {
        cons_jour.push(Math.floor(result.data[i][1] / 1000));
      }
      chargements++;
      essayerAfficherGraphique();
    }
  });

  // Chargement exportation
  jeedom.history.get({
    cmd_id: 65,
    dateStart: dateStartStr,
    dateEnd: dateEndStr,
    success: function (result) {
      for (let i = 0; i < result.data.length; i++) {
        export_jour.push(Math.floor(result.data[i][1] / 1000));
      }
      chargements++;
      essayerAfficherGraphique();
    }
  });

  // Chargement importation
  jeedom.history.get({
    cmd_id: 66,
    dateStart: dateStartStr,
    dateEnd: dateEndStr,
    success: function (result) {
      for (let i = 0; i < result.data.length; i++) {
        import_jour.push(Math.floor(result.data[i][1] / 1000));
      }
      chargements++;
      essayerAfficherGraphique();
    }
  });
}
</script>



      
 </figure>
</body>
</html>  

Deux remarques sur le code:

  • Inutile (et même « impossible ») de charger les librairies externes HighCharts comme c’était fait ici. Cela provoque une erreur « Directive de Content Security Policy ». HighCharts étant désormais intégré à Jeedom, nul besoin de ces appels externes comme expliqué par @Aurelien
  • Je n’ai pas réussi (et ChatGPT non plus) à faire en sorte que le graphique soit peuplé par défaut sur un créneau de dates données (par exemple de J-7 à J-1). C’est une affaire de chargement asynchrone mais le graphique « arrive » vide et je dois préciser les dates « à la main » pour qu’il se peuple.

Une remarque sur le contenu des énergies journalières affichées:

  • Je me serais attendu à ce que production + importation soit un peu supérieur à consommation + exportation, à cause des pertes. Or j’ai systématiquement le contraire. Je n’ai pas d’explication satisfaisante mais je vais vérifier que les données intégrées (en sommes par jour) considèrent toutes bien les mêmes périodes de temps.

Voilà, merci à tous ceux qui ont supporté avec navrement mes errements et commentaires doux-amers. Ceci dit, avant que Jeedom devienne un produit grand public, il risque de se passer du temps.

En espérant que cela serve.

2 « J'aime »

Bravo et merci !
Peux-tu préciser la mise en œuvre ?
Il suffit de faire un copier/coller du code, comment/ou indiquer les sources de données ?
Merci par avance

Oui bien sûr.

Il faut installer le plugin plugin-htmldisplay et l’activer .
On se rend ensuite sur la page du plugin :

Puis on créé un nouvel « équipement » qu’on appelle comme on veut (ici « ma visualisation »)

Il faudra ensuite, comme d’habituden l’activer et le rendre visible sur la page qu’on veut:

Ensuite, on colle le code indiqué dans l’éditeur de l’onglet « dashboard » si on veut que cette visualisation apparaisse sur le dashboard ou « mobile » si on veut qu’elle apparaisse sur le mobile (ou les deux).

Les sources de données sont les numéros de commande spécifiques aux équipements et sont spécifiées par les commandes du type :

// Chargement production
  jeedom.history.get({
    cmd_id: 42,
    dateStart: dateStartStr,
    dateEnd: dateEndStr,
    success: function (result) {
      for (let i = 0; i < result.data.length; i++) {
        const d = new Date(result.data[i][0]);
        timestamps.push(d.getTime());
        prod_jour.push(Math.floor(result.data[i][1] / 1000));
      }
      chargements++;
      essayerAfficherGraphique();
    }
  });

Ici, le numéro 42 correspond au numéro de la commande « production jour » de ma passerelle enphase:

L’avantage de cette librairie highcharts est qu’elle permet de rassembler les séries en autant de catégories qu’on veut (enfin, je pense, je n’ai pas testé). Moi j’ai utilisé deux catégories « In » et « Out » mais on peut sans doute en faire autant qu’on veut.

Bonne adaptation.

3 « J'aime »

Merci c’est clair !

1 « J'aime »

Ce sujet a été automatiquement fermé après 24 heures suivant le dernier commentaire. Aucune réponse n’est permise dorénavant.