Créer et personnaliser ses widgets : guide et exemples

Salut

Voici un post dédié à l’utilisation du plugin tools

Tools :Générateurs de Widget pour les commandes et template/modèles pour les tuiles

Partage de widgets possible avec la fonction import/export du plugin

Ce post n’est pas un centre d’aide du plugin , je ne répondrai pas aux demandes d’aides sur ce fil

:backhand_index_pointing_right: Pour toute question ou problème, merci donc d’ouvrir un sujet dédié

Sommaire

1. Les commandes

1.1 Les commande infos

1.2 Les commande actions

2. Les équipements

2.1 Les équipements personnalisables

2.2 Les équipements inclus au plugin (Les tuiles)

:backhand_index_pointing_right: Pour toute question ou problème, merci d’ouvrir un sujet dédié afin de garder ce fil lisible

1 « J'aime »

Commande info numeric simple

resultat attendu

  • Cliquer sur le plus et sélectionner le template numeric tile du core

  • Cliquer sur créer et donner un nom à l’équipement qui est créer . Activer l’équipement et aller dans l’onglet Personnalisation

  • A gauche les paramètres et a droite la fenêtre d’édition . Pour le moment c’est le code du widget du core . Pour tester , on va assigner le widget à une commande en cliquant sur le bouton
    image

  • Sélectionner la commande dans la fenêtre qui s’ouvre. Elle apparait ensuite dans l’onglet commande. En cliquant sur l’onglet prévisualisation vous aller voir le widget de base , celui du core

image

  • Dans les parametres optionnels selectionner couleur clique sur ajouter les paramtres e facon a avoir

  • On change le html/js dans la fenêtre d’édition
Afficher le code
<div class="cmd cmd-widget #history#" data-type="info" data-subtype="numeric" data-template="tile" data-cmd_id="#id#" data-cmd_uid="#uid#" data-version="#version#" data-eqLogic_id="#eqLogic_id#">
  <div class="title #hide_name#">
    <div class="cmdName">#name_display#</div>
  </div>
  <div class="content" style="display:flex;align-items:center;gap:6px;margin-top:4px;">
    <i class="icon_indicator #icon_temp#" style="font-size:1.4em;color:#color_temp#;"></i>
    <div>
      <span class="value state" style="font-size:1.2em;font-weight:bold;"></span> <span class="unit"></span>
    </div>
  </div>
  <div class="timeCmd label label-default #history#" data-type="info"></div>
  <div class="cmdStats #hide_history#">
    <span title="{{Minimum}}" class="tooltips">#minHistoryValue#</span>|<span title="{{Moyenne}}" class="tooltips">#averageHistoryValue#</span>|<span title="{{Maximum}}" class="tooltips">#maxHistoryValue#</span> <i class="#tendance#"></i>
  </div>
  <script>
    jeedom.cmd.addUpdateFunction('#id#', function(_options) {
      if (is_object(cmd = document.querySelector('.cmd[data-cmd_uid="#uid#"]'))) {
        cmd.querySelector('.content').setAttribute('title', '{{Date de valeur}}: ' + _options.valueDate + '<br>{{Date de collecte}}: ' + _options.collectDate)
        if ('#time#' == 'duration' || '#time#' == 'date') {
          jeedom.cmd.displayDuration(_options.valueDate, cmd.querySelector('.timeCmd'), '#time#')
        }

        var iconEl = cmd.querySelector('.icon_indicator')
        var colorDefault = '#color_temp#' || '#62d5ff'
        var colorHot = '#color_hot#' || '#ff4444'
        var colorCold = '#color_cold#' || '#4488ff'
        var thresholdHot = parseFloat('#seuil_haut#') || 30
        var thresholdCold = parseFloat('#seuil_bas#') || 19

        if (is_numeric(_options.value)) {
          cmd.querySelector('.value').innerHTML = _options.display_value
          cmd.querySelector('.unit').innerHTML = _options.unit
          var val = parseFloat(_options.value)
          if (iconEl) {
            if (val >= thresholdHot) {
              iconEl.style.color = colorHot
            } else if (val <= thresholdCold) {
              iconEl.style.color = colorCold
            } else {
              iconEl.style.color = colorDefault
            }
          }
        } else {
          cmd.querySelector('.value').innerHTML = '<span class="label label-danger">' + _options.value + '</span>'
        }

        if (_options.alertLevel == 'warning') {
          cmd.querySelector('.value').innerHTML = '<span class="label label-warning"><i class="fas fa-bell"></i> ' + _options.value + '</span>'
        } else if (_options.alertLevel == 'danger') {
          cmd.querySelector('.value').innerHTML = '<span class="label label-danger"><i class="fas fa-exclamation"></i> ' + _options.value + '</span>'
        }
      }
    })

    jeedom.cmd.refreshValue([{ cmd_id: '#id#', value: '#value#', display_value: '#state#', valueDate: '#valueDate#', collectDate: '#collectDate#', alertLevel: '#alertLevel#', unit: '#unite#' }])
  </script>
</div>

  • sauvegarder , prévisualiser

image

  1. Tooltip : met à jour le titre avec les dates de valeur et de collecte
  2. Durée/date : si le paramètre time est configuré, affiche la durée ou la date
  3. Selon la valeur :
    - Affiche la valeur + l’unité
    - Compare la valeur aux seuils :
   >= 30 (seuil_hot) → icône en rouge (#ff4444)                                                                                           
   <= 19 (seuil_cold) → icône en bleu (#4488ff)
  - Entre les deux → icône en couleur par défaut (#62d5ff)                                                                                       
  1. Si la valeur n’est pas numérique : affiche un badge rouge d’erreur
  2. Si alertLevel du core est warning ou danger : écrase l’affichage avec un badge coloré + icône

Et à chaque update des commandes assignées , l’icône change de couleur dynamiquement selon les seuils.

:up_arrow: Retour au sommaire

Commande action

Objectifs : Créer un widget d’action binaire on/off illustré par des images (On et off), pour contrôler un équipement ou un groupe d’équipements (Plugin groupe dans mon cas )

Pour ceux qui ont l’ancienne version du plugin , ce widget est basé sur sapin_anime

| Résultat attendu | image253x71

  • Selon le post précédent , vous créer l’équipement selon le widget du core cmd.action.other.default. Vous activer l’équipement .

  • En dehors des paramètres , il va permettre d’utiliser la gestion des images

  • Une fois l’équipement créé , direction l’onglet personnalisation

  • Créer 4 paramètres optionnels img_on , img_off , size (taille de l’cone sur le widget) et icon_light

  • sauvegarder

  • vous cliquer sur le bouton pour uploader les images et vous uploader l’image ON et OFF en cliquant sur le bouton « envoyer »

image

  • Vous copier le lien de chaque image et vous l’ajouter dans l’input prévu On ou Off

  • ce qui doit donner ensuite

  • Adapter le code dans la fenêtre d’édition (35 lignes…)
Afficher le code

<div class="cmd cmd-widget" data-type="action" data-subtype="other" data-cmd_id="#id#" data-cmd_uid="#uid#" data-version="#version#" data-eqLogic_id="#eqLogic_id#" style="display:inline-flex;flex-direction:row;flex-wrap:nowrap;align-items:center;gap:8px;white-space:nowrap;">
    <i class="#icon_light# fa-2x"></i> Lumières salon : 
    <span class="state_label" style="font-size:1.2em;white-space:nowrap;"></span>
    <img class="img_state cursor" style="width:#size#px;height:auto;"/>
  <script>
    (function() {
      var _uid = '#uid#'
      var _imgOn = '#img_on#'
      var _imgOff = '#img_off#'

      jeedom.cmd.addUpdateFunction('#id#', function(_options) {
        var cmd = document.querySelector('.cmd[data-cmd_uid="' + _uid + '"]')
        if (!is_object(cmd)) return
        var isOn = (_options.display_value == '1' || _options.display_value == 'on')

        var _cmdName = jeedom.cmd.normalizeName('#name#')
        if (isOn) {
          cmd.style.display = (_cmdName == 'on') ? 'none' : 'inline-flex'
          cmd.querySelector('.img_state').src = _imgOn
          cmd.querySelector('.state_label').textContent = '{{Allumé}}'
        } else {
          cmd.style.display = (_cmdName == 'off') ? 'none' : 'inline-flex'
          cmd.querySelector('.img_state').src = _imgOff
          cmd.querySelector('.state_label').textContent = '{{Éteint}}'
        }
      })

      jeedom.cmd.refreshValue([{cmd_id:'#id#', value:'#value#', display_value:'#state#', valueDate:'#valueDate#', collectDate:'#collectDate#', alertLevel:'#alertLevel#', unit:'#unite#'}])

      document.querySelector('.cmd[data-cmd_uid="' + _uid + '"]')?.querySelector('.img_state').addEventListener('click', function() {
        jeedom.cmd.execute({id: '#id#'})
      })
    })()
  </script>
</div>

  • sauvegarder

  • Maintenant on assigne le widget aux 2 commandes on et off (voir post précédent)

  • Et on a bien le résultat attendu (selon l’état)

| ON | image253x71

| OFF | image

Explications

Le widget :

  • affiche une icône définie par le paramètre optionnel #icon_light# ;
  • affiche un texte indiquant l’état, « Allumé » ou « Éteint », défini dans le JavaScript ;
  • affiche une image cliquable qui change selon l’état ;
  • adapte la taille de l’image grâce au paramètre #size# défini dans le code ;
  • masque automatiquement le bouton ON lorsque l’équipement est déjà allumé et, inversement, le bouton OFF lorsqu’il est éteint ;
  • utilise les fonctions du core Jeedom jeedom.cmd.execute() pour exécuter les actions et jeedom.cmd.addUpdateFunction() pour mettre à jour dynamiquement le widget.

:up_arrow: Retour au sommaire

Equipements

  • Objectifs : Créer un équipement en utilisant les commandes infos/actions d’autres plugins. Récupérer Le nom de l’utilisateur actuel et le nombre de messages non lus dans Jeedom. Utiliser la commande action créer au post précédent

  • Résultat attendu

  • Créer un équipement en cliquant sur le plus . Choisir un nom , activer et rendre visible l’équipement

  • Dans le fenêtre d’édition , à la création le code correspond a celui du core par défaut

  • Remplacer par le code suivant

Voir le code
<div class="eqLogic eqLogic-widget allowResize allowReorderCmd #custom_layout# #eqLogic_class#" data-eqType="#eqType#" data-eqLogic_id="#id#"
   data-eqLogic_uid="#uid#" data-version="#version#" style="width: #width#;height: #height#;#style#" 
  data-translate-category="#translate_category#" data-category="#category#" data-tags="#tags#">
      <center class="widget-name"><a href="#eqLink#" style="font-size : 1.1em;#hideEqLogicName#">#name_display# <span 
  class="object_name">#object_name#</span></a></center>
      <div class="#isVerticalAlign#">                                                                                                          
          <center>                                                                                                                              
              <div style="padding:12px 16px;text-align:left;font-family:inherit;color:var(--txt-color);">                                    
                  <div style="margin-bottom:10px;">                                                                                            
                      <span id="greeting_#uid#" style="font-size:20px;font-weight:700;"></span>                                              
                      <div id="date_#uid#" style="font-size:12px;opacity:0.6;margin-top:2px;"></div>                                           
                  </div>  
                 
                  <div style="display:flex;gap:16px;flex-wrap:wrap;margin-bottom:10px;">                                                       
                      <div style="display:flex;align-items:center;gap:8px;" title="Température extérieure">                                  
                          <i class="#icon_temp#" style="font-size:18px;opacity:0.7;"></i>                                                       
                          <span style="font-size:22px;font-weight:600;">#temp_ext# °C</span>                                                 
                      </div>                                                                                                                   
                      <div style="display:flex;align-items:center;gap:8px;" title="Température intérieure">                                    
                          <i class="fas fa-home" style="font-size:18px;opacity:0.7;"></i>                                                      
                          <span style="font-size:22px;font-weight:600;">#temp_int# °C</span>                                                   
                      </div>                                                                                                                   
                      <div style="display:flex;align-items:center;gap:8px;" title="Humidité">                                                  
                          <i class="fas fa-tint" style="font-size:18px;opacity:0.7;"></i>
                          <span style="font-size:22px;font-weight:600;">#humidity# %</span>                                                    
                      </div>                                                                                                                 
                  </div>                                                                                                                       
                  <div style="display:flex;gap:16px;font-size:12px;opacity:0.7;">
                      <span><i class="fas fa-arrow-up" style="color:orange;"></i> #sunrise#</span>                                             
                      <span><i class="fas fa-arrow-down" style="color:steelblue;"></i> #sunset#</span>                                       
                      <span><i class="fas fa-envelope"></i> <span id="messages_#uid#">0</span></span>                                          
                  </div>                
              </div>                                                                                                                           			
          </center>                                                                                                                            
      </div>  
      #on# #off#
  </div>                                                                                                                                       
                                                                                                                                            
  <script>
  (function(){
      var h = new Date().getHours();
      var colorDay = '#color_day#' || '';
      var colorNight = '#color_night#' || '';
      var color = (h >= 7 && h < 21) ? colorDay : colorNight;    
      var greet = h < 6 ? 'Bonne nuit' : h < 12 ? 'Bonjour' : h < 18 ? 'Bon après-midi' : 'Bonsoir';
      var el = document.getElementById('greeting_#uid#');
      var login = typeof user_login !== 'undefined' ? user_login : '';

      if (el) {
          el.textContent = greet + (login ? ' ' + login : '');
          el.style.color = color;
      }
      var d = new Date();                                                                                                                      
      var jours = ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'];                                                         
      var mois = ['janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre'];               
      var dateEl = document.getElementById('date_#uid#');                                                                                      
      if (dateEl) dateEl.textContent = jours[d.getDay()] + ' ' + d.getDate() + ' ' + mois[d.getMonth()] + ' ' + d.getFullYear(); 
      jeedom.message.number({                                                                                                                  
          success: function(data) {       
              var msgEl = document.getElementById('messages_#uid#');                                                                           
              if (msgEl) msgEl.textContent = data;                                                                                             
          }                               
      });                                                                                                                                      
  })();                                                                                                                                        
  </script>
  • Créer ensuite les commandes comme suit en sélectionnant les commandes que vous souhaitez faire apparaitre

  • Le nom que vous donner à la commande se retrouve dans le code au entre 2 # . Par exemple #temp_ext#. le plugin en utilisant des fonctions du core se chargera de faire les changements

Explications des options :

image

  1. La première checkbox permet de mettre la commande en listener c’est-à-dire qu’e cela permettra de mettre à jour la tuile en temps réel
  2. La deuxième permet d’afficher le template de la commande et la troisième seulement la valeur
  • Vous remarquez que pour les commandes qui changent souvent je les ai mis en listener et je récupère la valeur
    L’horaire de lever et coucher de soleil récupère juste l’information. Pas besoin de listener
    Pour la commande action créer précédemment je sélectionne les 2 et je coche le template ( pas besoin du listener dans ce cas là )

Explication du code , utilisation des fonctions de jeedom :

      jeedom.message.number({                                                                                                                  
          success: function(data) {       
              var msgEl = document.getElementById('messages_#uid#');                                                                           
              if (msgEl) msgEl.textContent = data;                                                                                             
          }                               
      });
  • Récupère le nombre de message non lus dans jeedom et l’insère en texte dans l’élément du DOM avec l’id « messages_#uid# » , le span ici

  • Le nom de l’utilisateur est récupéré via

user_login

(variable du core)

  • Une fois remplit tous les paramètres , enregistrer et prévisualiser . Tuile qui sera visible sur le dashboard

Et sur un design on peut masque le nom et rendre la tuile transparente

:up_arrow: Retour au sommaire

1 « J'aime »

Commande info circle

| Résultat attendu |

  • Cliquer sur le plus et sélectionner le template numeric tile du core (voir le premier post si besoin sur une comme info numeric simple)
  • Cliquer sur créer et donner un nom à l’équipement qui est crée . Activer l’équipement et aller dans l’onglet Personnalisation

  • Remplacer le code par celui-ci

Afficher le code
<div class="cmd cmd-widget #history#" data-type="info" data-subtype="numeric" data-template="tile" data-cmd_id="#id#" data-cmd_uid="#uid#"   
  data-version="#version#" data-eqLogic_id="#eqLogic_id#">                                                                                     
    <div class="title #hide_name#">                                                                                                            
      <div class="cmdName">#name_display#</div>                                                                                                
    </div>                                                                                                                                   
    <div class="content">
      <div class="ring-wrap">
        <svg viewBox="0 0 100 100">                                                                                                            
          <circle class="ring-track" cx="50" cy="50" r="40" />
          <circle class="ring-fill" cx="50" cy="50" r="40" />                                                                                  
        </svg>                                                                                                                                 
        <div class="ring-center">
          <span class="value"></span>                                                                                                          
          <span class="ring-unit">#unite#</span>                                                                                             
        </div>
      </div>
    </div>                                                                                                                                     
    <div class="timeCmd label label-default #history#" data-type="info"></div>
    <template>                                                                                                                                 
      <div>min : 0 ({{Valeur minimum}})</div>                                                                                                
      <div>max : 100 ({{Valeur maximum}})</div>
      <div>color_min : #55efc4 ({{Couleur de min a valeur}})</div>                                                                             
      <div>color_max : #2d3436 ({{Couleur de valeur a max}})</div>
      <div>thickness : 8 ({{Epaisseur anneau}})</div>                                                                                          
      <div>time : duration|date ({{Affiche duree ou date}})</div>                                                                            
    </template>                                                                                                                                
    <script>                                                                                                                                 
      ;(function(){                                                                                                                            
        var cmd = document.querySelector('.cmd[data-cmd_uid="#uid#"]');                                                                      
        if (!cmd) return;

        var C = 2 * Math.PI * 40;                                                                                                              
        var track = cmd.querySelector('.ring-track');
        var fill = cmd.querySelector('.ring-fill');                                                                                            
        var thickness = parseFloat('#thickness#') || 8;                                                                                      

        track.setAttribute('stroke-width', thickness);                                                                                         
        fill.setAttribute('stroke-width', thickness);
        track.setAttribute('stroke-dasharray', C);                                                                                             
        fill.setAttribute('stroke-dasharray', C);                                                                                            
        fill.setAttribute('stroke-dashoffset', C);

        if (CSS.supports('color', '#color_max#')) track.style.stroke = '#color_max#';                                                          
        if (CSS.supports('color', '#color_min#')) fill.style.stroke = '#color_min#';
                                                                                                                                               
        function updateRing(val) {                                                                                                             
          var min = parseFloat('#min#') || 0;
          var max = parseFloat('#max#') || 100;                                                                                                
          var v = Math.max(min, Math.min(max, parseFloat(val) || 0));                                                                        
          var pct = (v - min) / (max - min);
          fill.setAttribute('stroke-dashoffset', C * (1 - pct));                                                                               
        }
                                                                                                                                               
        jeedom.cmd.addUpdateFunction('#id#', function(_o) {                                                                                  
          var cmd = document.querySelector('.cmd[data-cmd_uid="#uid#"]');
          if (!cmd) return;                                                                                                                    
          cmd.querySelector('.content').setAttribute('title', '{{Date de valeur}}: ' + _o.valueDate + '<br>{{Date de collecte}}: ' +
  _o.collectDate);                                                                                                                             
          if ('#time#' == 'duration' || '#time#' == 'date') {                                                                                
            jeedom.cmd.displayDuration(_o.valueDate, cmd.querySelector('.timeCmd'), '#time#');                                                 
          }
          if (is_numeric(_o.value)) {                                                                                                          
            cmd.querySelector('.value').innerHTML = _o.display_value;                                                                        
            updateRing(_o.value);
          } else {
            cmd.querySelector('.value').innerHTML = '<span class="label label-danger">' + _o.value + '</span>';
          }                                                                                                                                    
          if (_o.alertLevel == 'warning') {
            cmd.querySelector('.value').innerHTML = '<span class="label label-warning"><i class="fas fa-bell"></i> ' + _o.value + '</span>';   
          } else if (_o.alertLevel == 'danger') {                                                                                            
            cmd.querySelector('.value').innerHTML = '<span class="label label-danger"><i class="fas fa-exclamation"></i> ' + _o.value +        
  '</span>';
          }                                                                                                                                    
        });                                                                                                                                  

        jeedom.cmd.refreshValue([{cmd_id:'#id#',value:'#value#',display_value:'#state#',valueDate:'#valueDate#',collectDate:'#collectDate#',alertLevel:'#alertLevel#',unit:'#unite#'}]);
      })();                                                                                                                                    
    </script>                                                                                                                                
    <style>
      .cmd[data-cmd_uid="#uid#"] .ring-wrap {
        position: relative; width: 90px; height: 90px; margin: 4px auto;
      }                                                                                                                                        
      .cmd[data-cmd_uid="#uid#"] .ring-wrap svg {
        width: 100%; height: 100%; transform: rotate(-90deg);                                                                                  
      }                                                                                                                                      
      .cmd[data-cmd_uid="#uid#"] .ring-track,
      .cmd[data-cmd_uid="#uid#"] .ring-fill {                                                                                                  
        fill: none; stroke-linecap: round;
      }                                                                                                                                        
      .cmd[data-cmd_uid="#uid#"] .ring-fill {                                                                                                
        transition: stroke-dashoffset 0.6s ease;                                                                                               
      }
      .cmd[data-cmd_uid="#uid#"] .ring-track { stroke: #2d3436; }                                                                              
      .cmd[data-cmd_uid="#uid#"] .ring-fill { stroke: #55efc4; }                                                                             
      .cmd[data-cmd_uid="#uid#"] .ring-center {                                                                                                
        position: absolute; top: 0; left: 0; width: 100%; height: 100%;
        display: flex; flex-direction: column; align-items: center; justify-content: center;                                                   
      }                                                                                                                                      
      .cmd[data-cmd_uid="#uid#"] .value {
        font-size: 18px; font-weight: 700; color: var(--txt-color);                                                                            
      }
      .cmd[data-cmd_uid="#uid#"] .ring-unit {                                                                                                  
        font-size: 10px; color: var(--txt-color); opacity: 0.6;                                                                              
      }                                                                                                                                        
    </style>
  </div> 
  • Apres avoir enregistré vous devriez avoir les paramètres optionnels dans la partie de configuration à droite , avec les valeurs par defaut

  • et si vous dépliez l’information en haut vous aurez le descriptif des paramètres

  • Enregistrer et assigner le template à une commande en cliquant sur le bouton Appliquer

  • image

  • Selectionner la ou les commandes pour lesquelles vous voulez ce template

  • Il y a des barres de recherche par nom d’objet , équipements et commande . Choisissez vue dashboard et/ou mobile . Cliquer sur le bouton valider

  • Dans l’onglet commande vous verrez les commandes selectionnées
    image

  • Une fois enregistré vous pouvez Prévisualisez ou sur un design

:up_arrow: Retour au sommaire

  • Objectif: Appliquer un template du plugin pour un équipement de Type lumière

  • Résultat attendu

:warning: Attention

Si les generic types ne sont pas configurés sur l’équipement lumière, le widget sera vide. C’est la condition obligatoire.

  • cliquer sur le + et selectionner l’équipement light_card

  • Une fois l’équipement créé , il faut juste sélectionner l’équipement source

image

  • On peut ensuite paramétrer l’icone , les pastilles , la couleur de l’icone On

  • Le resultat equipement ON et OFF