Сниппеты

О сниппетах

Сниппеты — это небольшие фрагменты кода, пригодные для повторного использования.

В системе intraHouse можно использовать сниппеты для получения значений устройств без привязки к каналам плагинов. То есть это легковесная замена плагина.

Данные можно получить, используя http-запрос, или запуская команду терминала, или считывая файл.

Например, в ОС Linux многие значения можно получить, просто читая файл.

Пример 1. Сниппет для чтения температуры CPU из файла /sys/class/thermal/thermal_zone0/temp

/**
* New snippet
*/

const fs = require('fs');

module.exports = callback => {
   fs.readFile('/sys/class/thermal/thermal_zone0/temp', 'utf8', (err, data) => {
      if (err) {
        callback(err.code);  
      } else if (isNaN(data)) {
        callback('Result '+data+' is not a number!'); 
      } else {
        callback(null, Math.round(Number(data) / 1000));
      }
    });
};

Примечание: Деление на 1000 выполняется, так как из файла читается целое число в тысячных градуса.

Часто данные можно получить, запуская терминальную команду.

Пример 2. Сниппет для получения температуры на RPI с использованием команды vcgencmd

/**
* New snippet
*/

module.exports = callback => {
  require('child_process').exec('vcgencmd measure_temp', (error, stdout) => {
    let value;
    if (!error && stdout && stdout.substr(0, 4) == 'temp') {
      value = parseFloat(stdout.split('=').pop()); 
    }
    callback( error || value === undefined, value);
  });
};

Использование сниппетов

Сниппет создается прямо в таблице устройств.

На вкладке «Дополнительно» добавлен чекбокс: Для получения значений использовать сниппет

Внимание! Сниппет можно использовать, если устройство не привязано к каналу плагина.

Если поставить галочку, будет открыто свойство Период запуска сниппета, а в нижнем окне будет доступен раздел Сниппет, который является редактором кода. Здесь надо добавить код сниппета.

Этот код будет вызываться циклически с заданным периодом.

Создание кода сниппета

Код создается в виде модуля, экспортирующего функцию. Аргумент callback — это функция обратного вызова, которая вернет результат в систему.

Никаких ограничений по структуре и синтаксису модуля нет. Предпочтительно использование асинхронных команд.

Ниже Пример 2, но с использованием старого синтаксиса JavaScipt. В первом варианте используются стрелочные функции нового синтаксиса ES6. Допустимы оба варианта.

/**
* New snippet
*/

module.exports = function(callback) {
  require('child_process').exec('vcgencmd measure_temp', function (error, stdout) {
    let value;
    if (!error && stdout && stdout.substr(0, 4) == 'temp') {
      value = parseFloat(stdout.split('=').pop()); 
    }
    callback( error || value === undefined, value);
  });
};

Возврат значения

Как принято в Node.js, функция обратного вызова callback возвращает ошибку в первом аргументе.

То есть, чтобы передать значение value, нужно вызвать callback одним из этих способов:

callback(null, value);
callback(0, value);
callback('', value);

Возврат ошибки

Если в первом аргументе передать не пустое значение и не ноль, это будет означать ошибку, второй аргумент будет проигнорирован.

Можно вернуть текст ошибки, тогда он появится в журнале устройства с добавлением ‘Ошибка!’

Пример 3. Достаем температуру с погодного сайта Gismeteo

/**
* New snippet
*/

module.exports = callback => {
  const regexp = /{"temperature":{"air":{"C":(.*?),"F"/gm;

  require('https').get('https://www.gismeteo.ru/weather-moscow-4368/now/', res => {
    if (res.statusCode != 200) {
      res.resume();
      callback("statusCode="+res.statusCode); // Сайт вернул статус ошибки, данные не получены
      return;
    }

    let rawData = '';
    res.on('data', chunk => {
      rawData += chunk;
    });
    res.on('end', () => {
      const arr = regexp.exec(rawData);
      if (arr && arr.length>1) {
        callback(null, arr[1]);  // Данные получены, парсинг удачен 
      } else {
        callback("Regexp вернул null");  // Данные получены, но парсинг неудачен 
      }
    });
  });
};