Plugin Template Documentation

You can find an empty plugin database here

Plugin Development Part 1: The Directory Structure

Here is its structure: first, a folder named after your plugin (more precisely, its unique identifier), which must contain the following subfolders:

As for the file naming convention, here are the Requirements:

Here are the recommendations:

Plugin Creation Part 2: plugin info folder

info.json

See here

install.php

File containing installation instructions for a plugin:

It is composed as follows:

The first commented section contains the license (which is better). The one used here states that the file belongs to Jeedom and that it is open source. Next comes the inclusion of the Jeedom core (this provides access to internal functions). Then come the three functions:

Example:

<?php
/* This file is part of Jeedom.
 *
 * Jeedom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jeedom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jeedom. If not, see <http://www.gnu.org/licenses/>.
 */
require_once dirname(__FILE__) . '/../../../core/php/core.inc.php';

function openzwave_install() {
    $cron = cron::byClassAndFunction('zwave', 'pull');
    if (!is_object($cron)) {
        $cron = new cron();
        $cron->setClass('zwave');
        $cron->setFunction('pull');
        $cron->setEnable(1);
        $cron->setDeamon(1);
        $cron->setSchedule('* * * * *');
        $cron->save();
    }
}

function openzwave_update() {
    $cron = cron::byClassAndFunction('zwave', 'pull');
    if (!is_object($cron)) {
        $cron = new cron();
        $cron->setClass('zwave');
        $cron->setFunction('pull');
        $cron->setEnable(1);
        $cron->setDeamon(1);
        $cron->setSchedule('* * * * *');
        $cron->save();
    }
    $cron->stop();
}

function openzwave_remove() {
    $cron = cron::byClassAndFunction('zwave', 'pull');
    if (is_object($cron)) {
        $cron->remove();
    }
}
?>

configuration.php

File used to request configuration information from the user:

The file consists of:

Next comes the requested parameter (there may be several); this is standard Bootstrap syntax for forms. The only specific requirements are the class (configKey) on the parameter element, as well as the “data-l1key” attribute, which specifies the parameter’s name. To retrieve the parameter’s value elsewhere in the plugin, simply do the following: config::byKey(NOM_PARAMETRE, PLUGIN_ID)

Example:

<?php
/* This file is part of Jeedom.
 *
  * Jeedom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jeedom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
  * You should have received a copy of the GNU General Public License
 * along with Jeedom. If not, see <http://www.gnu.org/licenses/>.
  */

 require_once dirname(__FILE__) . '/../../../core/php/core.inc.php';
include_file('core', 'authentification', 'php');
if (!isConnect()) {
    include_file('desktop', '404', 'php');
    die();
 }
 ?>
 <form class="form-horizontal">
     <fieldset>
         <div class="form-group">
             <label class="col-lg-2 control-label">Zway IP</label>
             <div class="col-lg-2">
                 <input class="configKey form-control" data-l1key="zwaveAddr" />
             </div>
         </div>
         <div class="form-group">
             <label class="col-lg-4 control-label">Supprimer automatiquement les périphériques exclus</label>
             <div class="col-lg-4">
                 <input type="checkbox" class="configKey" data-l1key="autoRemoveExcludeDevice" />
             </div>
         </div>
         <div class="form-group">
             <label class="col-lg-4 control-label">J'utilise un serveur openzwave</label>
             <div class="col-lg-4">
                 <input type="checkbox" class="configKey" data-l1key="isOpenZwave" />
             </div>
         </div>
     </fieldset>
 </form>

Plugin Development Part 3: Desktop Folder

PHP

This folder contains the actual view. It must include the plugin’s configuration page (the one that appears when the user goes to Plugins → Categories → your plugin). We recommend naming this page after your plugin’s ID. It may also contain the dashboard (the page the user will find under Home → your plugin’s name).

All files in this folder must end with .php and must begin with:


<?php
if (!isConnect('admin')) {
    throw new Exception('{{401 - Accès non autorisé}}');
 }
 sendVarToJS('eqType', 'mail');
 ?>
 

Once you’re on this page, you’ll have PHP access to all of Jeedom’s core functions (see here ) as well as those of all installed modules, including yours.

Since all of these pages are views, they primarily use HTML syntax. For everything related to presentation, Jeedom relies mainly on Bootstrap, so all of the documentation is applicable.

To simplify plugin creation, you can include the JavaScript template script for plugins in your page:

<?php include_file('core', 'plugin.template', 'js'); ?>

Place this at the very bottom of your page; it is only useful on your plugin’s configuration page. This script reduces the required JavaScript to a single function (see the section on JS files).

On your configuration page, HTML syntax has been implemented to make your life easier. So for most plugins, all you’ll need to do is write HTML to store your information in the database and then reuse it in your class.

The syntax is quite simple: your element (input, select, etc.) must have the CSS class eqLogicAttr (or cmdAttr for commands) and an attribute specifying the property name:


<input type="text" class="eqLogicAttr form-control" data-l1key="name" placeholder="{{Nom de l'équipement mail}}"/>

For example, when loading data, Jeedom will populate the input field with the device name, and when saving, it will retrieve that value and store it back in the database. Here’s a quick tip: some properties are actually JSON strings in the database (which gives the plugin quite a bit of flexibility); in that case, all you need to do is:

<input class="eqLogicAttr form-control" data-l1key='configuration' data-l2key='fromName' />

For a list of device and command properties, click here (to see which properties are JSON, just look at the getter or setter; if it takes two parameters, then it’s JSON)

One last important point about the configuration page: it can contain as many devices and commands as needed. However, there are a few rules to follow:

All elements with the eqLogicAttr class must be contained within an element with the css eqLogic class. The same applies to elements with the css cmdAttr class, which must be contained within an element with the cmd class. All commands for a device must be contained within the element with the corresponding eqLogic class.

JS

All JS files must be located in the JS folder (easy!!!). We recommend naming it after your plugin’s ID (in the configuration section; for the panel, you can name it whatever you like). This JS file (the one for the plugin’s configuration) must contain at least one method, addCmdToTable, which takes the command object to be added as a parameter. Here’s a simple example:

function addCmdToTable(_cmd) {
    if (!isset(_cmd)) {
        var _cmd = {configuration: {}};
     }
    var tr = '';     tr += '';
     tr += '<input class="cmdAttr form-control input-sm" data-l1key="id" style="display : none;">';
    tr += '<input class="cmdAttr form-control input-sm" data-l1key="name">';     tr += '<input class="cmdAttr form-control input-sm" data-l1key="configuration" data-l2key="recipient">';     tr += '';
     tr += '<input class="cmdAttr form-control input-sm" data-l1key="type" value="action" style="display : none;">';
     tr += '<input class="cmdAttr form-control input-sm" data-l1key="subType" value="message" style="display : none;">';
     if (is_numeric(_cmd.id)) {
        tr += '<a class="btn btn-default btn-xs cmdAction" data-action="test"><i class="fa fa-rss"></i> </a>';
     }
     tr += '<i class="fa fa-minus-circle pull-right cmdAction cursor" data-action="remove"></i></td>';
    tr += '';
     $('#table_cmd tbody').append(tr);
    $('#table_cmd tbody tr:last').setValues(_cmd, '.cmdAttr');
}

You’ll notice that there is one line per command and that each line has the CSS class cmd. You can also see the elements that have the class cmdAttr.

Several important points:

One last point: a more comprehensive example using command types and subtypes:


function addCmdToTable(_cmd) {
    if (!isset(_cmd)) {
        var _cmd = {};
    }
     if (!isset(_cmd.configuration)) {
        _cmd.configuration = {};
    }
     var selRequestType = '<select style="width : 90px;" class="cmdAttr form-control input-sm" data-l1key="configuration" data-l2key="requestType">';
     selRequestType += '<option value="script">{{Script}}</option>';
     selRequestType += '<option value="http">{{Http}}</option>';
     selRequestType += '</select>';
    var tr = '';     tr += '<input class="cmdAttr form-control input-sm" data-l1key="name" style="width : 140px;">';
    tr += '<input class="cmdAttr form-control input-sm" data-l1key="id"  style="display : none;">';
    tr += '' + selRequestType;
    tr += '<div class="requestTypeConfig" data-type="http">';
    tr += '<input type="checkbox" class="cmdAttr" data-l1key="configuration" data-l2key="noSslCheck" />Ne pas vérifier SSL';
    tr += '</div>';
    tr += '';     tr += '';
    tr += '<span class="type" type="' + init(_cmd.type) + '">' + jeedom.cmd.availableType() + '</span>';
    tr += '<span class="subType" subType="' + init(_cmd.subType) + '"></span>';
    tr += '';     tr += '<textarea style="height : 95px;" class="cmdAttr form-control input-sm" data-l1key="configuration" data-l2key="request"></textarea>';
    tr += '<a class="btn btn-default browseScriptFile cursor input-sm" style="margin-top : 5px;"><i class="fa fa-folder-open"></i> {{Parcourir}}</a> ';
    tr += '<a class="btn btn-default editScriptFile cursor input-sm" style="margin-top : 5px;"><i class="fa fa-edit"></i> {{Editer}}</a> ';
    tr += '<a class="btn btn-success newScriptFile cursor input-sm" style="margin-top : 5px;"><i class="fa fa-file-o"></i> {{Nouveau}}</a> ';
    tr += '<a class="btn btn-danger removeScriptFile cursor input-sm" style="margin-top : 5px;"><i class="fa fa-trash-o"></i> {{Supprimer}}</a> ';
    tr += '<a class="btn btn-warning bt_shareOnMarket cursor input-sm" style="margin-top : 5px;"><i class="fa fa-cloud-upload"></i> {{Partager}}</a> ';
    tr += '</div>';
    tr += '';     tr += '';
    tr += '<input class="cmdAttr form-control tooltips input-sm" data-l1key="unite"  style="width : 100px;" placeholder="{{Unité}}" title="{{Unité}}">';
    tr += '<input class="tooltips cmdAttr form-control input-sm" data-l1key="configuration" data-l2key="minValue" placeholder="{{Min}}" title="{{Min}}"> ';
    tr += '<input class="tooltips cmdAttr form-control input-sm" data-l1key="configuration" data-l2key="maxValue" placeholder="{{Max}}" title="{{Max}}">';
    tr += '';     tr += '';
    tr += '<span><input type="checkbox" class="cmdAttr" data-l1key="isHistorized" /> {{Historiser}}<br/></span>';
    tr += '';     tr += '';
    if (is_numeric(_cmd.id)) {
    tr += '<a class="btn btn-default btn-xs cmdAction" data-action="test"><i class="fa fa-rss"></i> {{Tester}}</a>';
    }
    tr += '<i class="fa fa-minus-circle pull-right cmdAction cursor" data-action="remove"></i></td>';
    tr += '';
    $('#table_cmd tbody').append(tr);
    $('#table_cmd tbody tr:last').setValues(_cmd, '.cmdAttr');

    if (isset(_cmd.configuration.requestType)) {
        $('#table_cmd tbody tr:last .cmdAttr[data-l1key=configuration][data-l2key=requestType]').value(init(_cmd.configuration.requestType));
        $('#table_cmd tbody tr:last .cmdAttr[data-l1key=configuration][data-l2key=requestType]').trigger('change');
    }

     if (isset(_cmd.type)) {
        $('#table_cmd tbody tr:last .cmdAttr[data-l1key=type]').value(init(_cmd.type));
    }
     jeedom.cmd.changeType($('#table_cmd tbody tr:last'), init(_cmd.subType));
    initTooltips();
}

Here, we can see:

Other JavaScript functions can be used:

<?php include_file('desktop', 'weather', 'js', 'weather'); ?>

The first argument specifies the folder where it is located (note that this is the parent folder of the JS folder), the second is the name of your JavaScript file, the third tells Jeedom that it is a JS file, and the last specifies which plugin it is in.

CSS

This folder contains your CSS files (it shouldn’t be used too much); here’s how to include them on your page:

<?php include_file('desktop', 'weather', 'css', 'weather'); ?>

The first argument specifies the folder where it is located (note that this is the parent folder of the CSS folder), the second is the name of your CSS file, the third tells Jeedom that it is a CSS file, and the last specifies which plugin it is in.

The modal folder lets you store your PHP files used to display modals. Here’s how to call them from your main page (this code goes in a JavaScript file):

Here’s what you can see:


$('#md_modal').dialog({title: "{{Classe du périphérique}}"}).load('index.php?v=d&plugin=zwave&modal=show.class&id=' + $('.eqLogicAttr[data-l1key=id]').value()).dialog('open')

The first line lets you add a title to your modal

The second line loads your modal and the display. The syntax is quite simple: plugin, your plugin’s ID, modal, the name of your modal without the “php,” and then the parameters you want to pass to it

JS API

This isn’t a feature, but in the latest versions of Jeedom, it provides developers with a full JavaScript API (which eliminates the need to write AJAX calls all over the place). I’ll try to write an article explaining the various features, but you can already find the code here.

That covers the details of the desktop folder. I realize it’s not the most comprehensive (I’ll try to expand it based on the various requests I receive), but I hope it will help you get started on creating plugins for Jeedom.

Tips and Tricks

Cron Assistant

$('body').delegate('.helpSelectCron','click',function() {
  var el = $(this).closest('.schedule').find('.scenarioAttr[data-l1key=schedule]')
  jeedom.getCronSelectModal({},function (result) {
    el.value(result.value)
  })
})

When you click the assistant button, the input field where you can type appears, and then the assistant is launched. Once configuration is complete in the assistant, the result is retrieved and written to the previously selected input field

Plugin Development Part 4: core folder

By far the most important folder in your plugin, it can contain up to 4 subfolders.

Note: Throughout this section, your plugin’s ID will be referred to as: plugin_id

PHP

Contains the associated PHP files. I’ve gotten into the habit of including, for example, an include file if—of course—you have multiple class files or third-party files to include.

Template

This folder can contain two subfolders, “dashboard” and “mobile.” Jeedom automatically scans this folder for widgets, so if you’re using specific widgets, this is where you should place their HTML files.

i18n

This is where your translation should be located as a JSON file (it’s best to check out the plugin, for example, Z-Wave to view the file format)

AJAX

This folder is for all your AJAX files. Here is a sample AJAX file:

<?php

/* This file is part of Jeedom.
 *
 * Jeedom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jeedom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jeedom. If not, see <http://www.gnu.org/licenses/>.
 */

try {
    require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';
    include_file('core', 'authentification', 'php');

    if (!isConnect('admin')) {
        throw new Exception(__('401 - Accès non autorisé', __FILE__));
    }

    if (init('action') == 'votre action') {
        ajax::success($result);
    }

    throw new Exception(__('Aucune methode correspondante à : ', __FILE__) . init('action'));
    /*     * *********Catch exeption*************** */
} catch (Exception $e) {
    ajax::error(displayExeption($e), $e->getCode());
}
?>

class

This is a very important file—it’s the core of your plugin. This is where the two required classes for your plugin go:

The first one should inherit from the eqLogic class, and the second from cmd. Here is a template:

<?php

/* This file is part of Jeedom.
 *
 * Jeedom is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jeedom is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Jeedom. If not, see <http://www.gnu.org/licenses/>.
 */

/* * ***************************Includes********************************* */
require_once dirname(__FILE__) . '/../../../../core/php/core.inc.php';

class plugin_id extends eqLogic {

    /*     * *************************Attributs****************************** */


    /*     * ***********************Methode static*************************** */


    /*     * *********************Methode d'instance************************* */


    /*     * **********************Getteur Setteur*************************** */

}

class plugin_idCmd extends cmd {

    /*     * *************************Attributs****************************** */


    /*     * ***********************Methode static*************************** */


    /*     * *********************Methode d'instance************************* */


    /*     * **********************Getteur Setteur*************************** */

}

?>

For information on defining Jeedom classes, please refer to this website

The only required method is the instance method on the cmd_execute class. Here is an example using the S.A.R.A.H plugin:

public function execute($_options = array()) {
      if (!isset($_options['title']) && !isset($_options['message'])) {
          throw new Exception(__("Le titre ou le message ne peuvent être tous les deux vide", __FILE__));
      }
      $eqLogic = $this->getEqLogic();
      $message = '';
      if (isset($_options['title'])) {
          $message = $_options['title'] . '. ';
      }
      $message .= $_options['message'];
      $http = new com_http($eqLogic->getConfiguration('addrSrvTts') . '/?tts=' . urlencode($message));
      return $http->exec();
  }

Here’s a fairly simple but comprehensive example. The principle is as follows: if the command is an action or a piece of information (but not just an event, and its cache has expired), then Jeedom calls this method.

In our example here, this is a command to make S.A.R.A.H speak, where the plugin retrieves the parameters from $_options (note that this is an array and its attributes change depending on the command’s type: “color” for a “color” type, “slider” for a “slider” type, “title” and “message” for a “message” type, and empty for an “other” type).

That covers the required part; here are some additional features you can use (with examples):

toHtml($_version = ‘dashboard’)

This function can be used in the command system or in the device, depending on your needs. Here is an example for the device:

public function toHtml($_version = 'dashboard') {
    $replace = $this->preToHtml($_version);
    if (!is_array($replace)) {
        return $replace;
    }
    $version = jeedom::versionAlias($_version);
    $replace['#forecast#'] = '';
    if ($version != 'mobile' || $this->getConfiguration('fullMobileDisplay', 0) == 1) {
        $forcast_template = getTemplate('core', $version, 'forecast', 'weather');
        for ($i = 0; $i < 5; $i++) {
            $replaceDay = array();
            $replaceDay['#day#'] = date_fr(date('l', strtotime('+' . $i . ' days')));

            if ($i == 0) {
                $temperature_min = $this->getCmd(null, 'temperature_min');
            } else {
                $temperature_min = $this->getCmd(null, 'temperature_' . $i . '_min');
            }
            $replaceDay['#low_temperature#'] = is_object($temperature_min) ? $temperature_min->execCmd() : '';

            if ($i == 0) {
                $temperature_max = $this->getCmd(null, 'temperature_max');
            } else {
                $temperature_max = $this->getCmd(null, 'temperature_' . $i . '_max');
            }
            $replaceDay['#hight_temperature#'] = is_object($temperature_max) ? $temperature_max->execCmd() : '';
            $replaceDay['#tempid#'] = is_object($temperature_max) ? $temperature_max->getId() : '';

            if ($i == 0) {
                $condition = $this->getCmd(null, 'condition');
            } else {
                $condition = $this->getCmd(null, 'condition_' . $i);
            }
            $replaceDay['#icone#'] = is_object($condition) ? self::getIconFromCondition($condition->execCmd()) : '';
            $replaceDay['#conditionid#'] = is_object($condition) ? $condition->getId() : '';
            $replace['#forecast#'] .= template_replace($replaceDay, $forcast_template);
        }
    }
    $temperature = $this->getCmd(null, 'temperature');
    $replace['#temperature#'] = is_object($temperature) ? $temperature->execCmd() : '';
    $replace['#tempid#'] = is_object($temperature) ? $temperature->getId() : '';

    $humidity = $this->getCmd(null, 'humidity');
    $replace['#humidity#'] = is_object($humidity) ? $humidity->execCmd() : '';

    $pressure = $this->getCmd(null, 'pressure');
    $replace['#pressure#'] = is_object($pressure) ? $pressure->execCmd() : '';
    $replace['#pressureid#'] = is_object($pressure) ? $pressure->getId() : '';

    $wind_speed = $this->getCmd(null, 'wind_speed');
    $replace['#windspeed#'] = is_object($wind_speed) ? $wind_speed->execCmd() : '';
    $replace['#windid#'] = is_object($wind_speed) ? $wind_speed->getId() : '';

    $sunrise = $this->getCmd(null, 'sunrise');
    $replace['#sunrise#'] = is_object($sunrise) ? $sunrise->execCmd() : '';
    $replace['#sunid#'] = is_object($sunrise) ? $sunrise->getId() : '';
    if (strlen($replace['#sunrise#']) == 3) {
        $replace['#sunrise#'] = substr($replace['#sunrise#'], 0, 1) . ':' . substr($replace['#sunrise#'], 1, 2);
    } else if (strlen($replace['#sunrise#']) == 4) {
        $replace['#sunrise#'] = substr($replace['#sunrise#'], 0, 2) . ':' . substr($replace['#sunrise#'], 2, 2);
    }

    $sunset = $this->getCmd(null, 'sunset');
    $replace['#sunset#'] = is_object($sunset) ? $sunset->execCmd() : '';
    if (strlen($replace['#sunset#']) == 3) {
        $replace['#sunset#'] = substr($replace['#sunset#'], 0, 1) . ':' . substr($replace['#sunset#'], 1, 2);
    } else if (strlen($replace['#sunset#']) == 4) {
        $replace['#sunset#'] = substr($replace['#sunset#'], 0, 2) . ':' . substr($replace['#sunset#'], 2, 2);
    }

    $wind_direction = $this->getCmd(null, 'wind_direction');
    $replace['#wind_direction#'] = is_object($wind_direction) ? $wind_direction->execCmd() : 0;

    $refresh = $this->getCmd(null, 'refresh');
    $replace['#refresh_id#'] = is_object($refresh) ? $refresh->getId() : '';

    $condition = $this->getCmd(null, 'condition_now');
    $sunset_time = is_object($sunset) ? $sunset->execCmd() : null;
    $sunrise_time = is_object($sunrise) ? $sunrise->execCmd() : null;
    if (is_object($condition)) {
        $replace['#icone#'] = self::getIconFromCondition($condition->execCmd(), $sunrise_time, $sunset_time);
        $replace['#condition#'] = $condition->execCmd();
        $replace['#conditionid#'] = $condition->getId();
        $replace['#collectDate#'] = $condition->getCollectDate();
    } else {
        $replace['#icone#'] = '';
        $replace['#condition#'] = '';
        $replace['#collectDate#'] = '';
    }
    if ($this->getConfiguration('modeImage', 0) == 1) {
        $replace['#visibilityIcon#'] = "none";
        $replace['#visibilityImage#'] = "block";
    } else {
        $replace['#visibilityIcon#'] = "block";
        $replace['#visibilityImage#'] = "none";
    }
    $html = template_replace($replace, getTemplate('core', $version, 'current', 'weather'));
    cache::set('widgetHtml' . $_version . $this->getId(), $html, 0);
    return $html;
}

There are several interesting things here:

To convert the requested version to a dashboard or mobile view (e.g., “mview” becomes “mobile”), which allows you to add the names of objects to the views, for example.

$_version = jeedom::versionAlias($_version);

Retrieving a command template; in this case, the command template: plugins/weather/core/template/$_version/forecast.html (where $$_version$ is either “mobile” or “dashboard”)

$forcast_template = getTemplate('core', $_version, 'forecast', 'weather');

Here, replace the tags previously filled in the $replace section of the HTML to contain the values

$html_forecast .= template_replace($replace, $forcast_template);

This retrieves the command with the logical_id: temperature_min

$this->getCmd(null, 'temperature_min');

This allows you to set the value in the tag only if the command was successfully received

$replace['#temperature#'] = is_object($temperature) ? $temperature->execCmd() : '';

Important note: This retrieves the customizations made by the user on the General → Display page and reapplies them to the template

$parameters = $this->getDisplay('parameters');
if (is_array($parameters)) {
    foreach ($parameters as $key => $value) {
        $replace['#' . $key . '#'] = $value;
    }
}

Caching the widget: to ensure it loads faster on the next request, note the 0 here, which indicates an infinite cache lifetime; otherwise, the duration is in seconds (we’ll see in the next section how the weather plugin updates its widget).

cache::set('weatherWidget' . $_version . $this->getId(), $html, 0);

Finally, sending the HTML to Jeedom:

return $html;

You also need to tell Jeedom what customization options your widget supports. It’s a bit complex (though not too much), but it’s generally flexible and easy to set up.

It works the same way on your device or command; it’s a static attribute of the $_widgetPossibility class that must be a multidimensional array, but this is where things get complicated if one dimension of the array is set to true or false. In that case, it assumes that all possible children have that value (I’ll give an example).

First, here are the scenarios where you’ll need to use this: if your class that inherits from eqLogic or cmd has a toHtml function; otherwise, there’s no point in reading further.

Pre- and Post-Method

When creating or deleting your objects (devices, commands, or others) in Jeedom, Jeedom may call several methods before or after the action:

For example, using the weather plugin again, here’s how to create commands or update them after saving (this is a simplified example):

public function postUpdate() {
      $weatherCmd = $this->getCmd(null, 'temperature');
      if (!is_object($weatherCmd)) {
          $weatherCmd = new weatherCmd();
      }
      $weatherCmd->setName(__('Température', __FILE__));
      $weatherCmd->setLogicalId('temperature');
      $weatherCmd->setEqLogic_id($this->getId());
      $weatherCmd->setConfiguration('day', '-1');
      $weatherCmd->setConfiguration('data', 'temp');
      $weatherCmd->setUnite('°C');
      $weatherCmd->setType('info');
      $weatherCmd->setSubType('numeric');
      $weatherCmd->save();

      $cron = cron::byClassAndFunction('weather', 'updateWeatherData', array('weather_id' => intval($this->getId())));
      if (!is_object($cron)) {
          $cron = new cron();
          $cron->setClass('weather');
          $cron->setFunction('updateWeatherData');
          $cron->setOption(array('weather_id' => intval($this->getId())));
      }
      $cron->setSchedule($this->getConfiguration('refreshCron', '*/30 * * * *'));
      $cron->save();
}

The beginning is fairly standard—creating a command—but the end is more interesting, as it involves setting up a cron job that will call the method weather::updateWeatherData by passing the ID of the device to be updated every 30 minutes by default.

Here is the updateWeatherData method (also simplified):

public static function updateWeatherData($_options) {
  $weather = weather::byId($_options['weather_id']);
  if (is_object($weather)) {
      foreach ($weather->getCmd('info') as $cmd) {
        $weather->checkAndUpdateCmd($cmd,$cmd->execute());
      }
  }
}

Here we can see that when the call is made, the relevant device is retrieved, and then commands are executed to retrieve the values and update them if necessary.

A very important part:

$weather->checkAndUpdateCmd($cmd,$cmd->execute());

At the time of the function checkAndUpdateCmd (which notifies Jeedom of a new value update, triggering all necessary actions: updating the dashboard, checking scenarios…​),

For the command class, here’s a quick tip if you’re using the basic JS template. When sending device commands, Jeedom compares the commands and removes any that are in the base definition but not in the new device definition. Here’s how to avoid that:

public function dontRemoveCmd() {
  return true;
}

Finally, here are a few tips and tricks:

$eqLogic->batteryStatus(56);
Nous utilisons des cookies pour vous garantir la meilleure expérience sur notre site web. Si vous continuez à utiliser ce site, nous supposerons que vous en êtes satisfait.