Utiliser la librairie EcmaScript dans une WebExtension

Utiliser la librairie EcmaScript dans une WebExtension

Pour un projet interne, nous avons eu besoin d'écrire une extension pour Firefox qui devait interagir avec un module Yoctopuce. Étant donné que les extensions sont écrites en JavaScript, nous avons pu utiliser notre librairie EcmaScript. Du coup, on s'est dit que ça pourrait intéresser d'autres personnes et cette semaine on vous montre comment réaliser une Web Extension qui affiche la température d'un Yocto-Meteo dans la barre d’icônes de votre browser.



Les extensions pour browser sont en fait simplement des pages web écrites en HTML et JavaScript qui sont chargées automatiquement par le browser. En plus de pouvoir accéder au DOM habituel, les extensions ont accès à des API spécifiques qui permettent de modifier l'apparence et le comportement du browser.

Pratiquement tous les browsers permettent d'installer des extensions. Cependant, comme lors du début de JavaScript, il n'y a pas de véritable standard et chaque browser a implémenté les extensions à sa sauce. Les WebExtensions sont ce qui ressemble le plus à un standard et permettent d'écrire une extension compatible avec Chrome, Firefox et Opera.

Dans cet exemple, nous allons écrire une WebExtensions qui affiche un bouton dans la barre d'adresse du browser qui permet d'afficher la valeur courante d'un Yocto-Meteo.

Nous allons nous concentrer sur l'utilisation de la librairie Yoctopuce dans l’extension. Nous n'allons pas expliquer en détail comment fonctionne une WebExtension car la documentation proposée par Mozilla est très bien faite et est disponible dans de nombreuses langues: documentation WebExtension.

Mais, en gros, une WebExtension est simplement un répertoire avec un fichier JSON manifest.json qui référence des fichiers JavaScript et HTML à exécuter. Il est possible d’exécuter des scripts en arrière plan et d'autres uniquement lors de la pression d'un bouton.

Le fichier manifest.json



Dans cet exemple, nous devons définir un bouton qui va afficher la page popup.html et exécuter le script background-script.js en arrière plan. Le bouton est défini dans la section "browser_action", et prend en argument une icône et le nom de la page HTML à afficher.

La section background permet de charger tous les scripts qui doivent être exécutés dès que l'extension est chargée. Il faut donc ajouter les fichiers de notre librairie EcmaScript et le fichier "background-script.js".

Le fichier manifest.json:

{
  "manifest_version": 2,
  "name": "Yocto-Meteo-watcher",
  ...
  "browser_action": {
  "default_icon": "icons/icon_32.png",
  "default_popup": "popup.html"
  },
  ....
  "background": {
  "scripts": [
    "yoctolib/yocto_api.js",
    "yoctolib/yocto_temperature.js",
    "yoctolib/yocto_humidity.js",
    "yoctolib/yocto_pressure.js",
    "background-script.js"
  ]
  },
  ...
}



Le script background-script.js


Le script background-script.js commence par initialiser la librairie à l'aide de la méthode YAPI.RegisterHub en lui fournissant l'URL du YoctoHub ou du VirtualHub utilisé, et programme un timer qui va périodiquement rechercher la température du Yocto-Meteo et l'afficher à l'aide de la fonction chrome.browserAction.setBadgeText.

Lors de la première exécution, on initialise les variables globales ytemperature, yhumidity et ypressure avec les fonctions YFindXXX de la librairie. Pour les exécutions suivantes, on vérifie que le capteur de température est toujours branché, on récupère la valeur courante à l'aide de la méthode get_currentValue() et on l'affiche par dessus l’icône du bouton.


var ytemperature = null;
var yhumidity = null;
var ypressure = null;
var serial = "";

async function startDemo() {
  await YAPI.LogUnhandledPromiseRejections();
  // Setup the API to use the VirtualHub on local machine
  let url = await get_hub_url();
  let errmsg = new YErrorMsg();
  if (await YAPI.RegisterHub(url, errmsg) !== YAPI.SUCCESS) {
    console.log('Cannot contact ' + url + ': ' + errmsg.msg);
    return;
  }
  refresh();
}

async function refresh() {
  try {
    let errmsg = new YErrorMsg();
    if (yhumidity === null) {
      await YAPI.UpdateDeviceList(errmsg);
      yhumidity = YHumidity.FirstHumidity();
      if (yhumidity) {
        let module = await yhumidity.module();
        serial = await module.get_serialNumber();
        ytemperature = YTemperature.FindTemperature(serial + ".temperature");
        ypressure = YPressure.FindPressure(serial + ".pressure");
      }
    }
    let text = "";
    if (ytemperature && await ytemperature.isOnline()) {
      let temp = await ytemperature.get_currentValue();
      text = temp.toString();
    }
    chrome.browserAction.setBadgeText({text: text});
  } catch (e) {
    console.log(e);
    chrome.browserAction.setBadgeText({text: "!"});
  }
  timer = setTimeout(refresh, 1000);
}
...



On affiche la température dans l’icône de l'extension
On affiche la température dans l’icône de l'extension



La page popup.html


Il reste à écrire le contenu de la page popup.html qui doit afficher la valeur courante des trois capteurs du Yocto-Meteo. Cette page charge un fichier popup.js qui va mettre à jour le contenu de la page avec les valeurs des capteurs.

La page HTML:

...
<body>
<h1>Yocto-Meteo</h1>
<table>
    <tr class="element">
        <td class="icon" ><img src="../icons/temp_96.png" alt="Temperature"></td>
        <td><span class="sensorvalue" id="temp"></span></td>
    </tr>
    <tr class="element">
        <td class="icon"><img src="../icons/hum_96.png" alt="Humidity"></td>
        <td><span class="sensorvalue" id="hum"></span></td>
    </tr>
    <tr class="element">
        <td class="icon"><img src="../icons/cloud_96.png" alt="Pressure"></td>
        <td><span class="sensorvalue" id="pres"></span></td>
    </tr>
</table>
</body>
</html>



Le script popup.js rafraîchit périodiquement le contenu de la page avec la valeur des trois capteurs en utilisant les variables globales ytemperature, yhumidity et ypressure que nous avons définies dans le script. Pour accéder à ces variables, il faut utiliser la fonction chrome.extension.getBackgroundPage() qui retourne le contexte du script background-script.js.

async function updateSensor(sensor, tag) {
  if (sensor && sensor.isOnline()) {
    let value = await sensor.get_currentValue();
    document.getElementById(tag).innerHTML =
        value.toString() + " " + await sensor.get_unit();
  } else {
    document.getElementById(tag).innerHTML = "Offline";
  }
}

async function refresh() {
  let bgpage = chrome.extension.getBackgroundPage();
  await updateSensor(bgpage.ytemperature, "temp");
  await updateSensor(bgpage.yhumidity, "hum");
  await updateSensor(bgpage.ypressure, "pres");
  timer = setTimeout(refresh, 500);
}

document.addEventListener('DOMContentLoaded', async () => {
  await refresh();
});



En cliquant sur l'icône, on affiche l'état du Yocto-Meteo
En cliquant sur l'icône, on affiche l'état du Yocto-Meteo



Comme d'habitude, vous pouvez retrouver le code source complet sur GitHub:
https://github.com/yoctopuce-examples/web_extension_demo.

Tester et publier l'extension


La procédure pour installer et tester l'extension est différente pour chaque browser. Mais dans chaque browser il y a une option qui permet d'installer une extension depuis un répertoire local. Pour Firefox, par exemple, il faut acceder à l'URL about:debugging puis cliquer sur le bouton "Load temporary Add-on".

Dans tous les browsers, il est possible d'installer localement l'extension
Dans tous les browsers, il est possible d'installer localement l'extension



Pour publier une extension, là aussi la procédure est différente pour chaque browser, mais en général il faut ziper le contenu du répertoire et l'uploader sur le "store" du browser afin que le package soit signé et puisse être installé sur n'importe quelle machine.

Conclusion


Il est très facile d'utiliser notre librairie EcmaScript dans une WebExtension, il suffit de copier les fichiers et les référencer. Les seules difficultés que vous allez rencontrer sont des problèmes d'API qui ne sont pas toujours implémentées dans tous les browsers, heureusement la librairie Yoctopuce n'utilise aucune de ces APIs.

Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.