Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Русский
    3. ioBroker
    4. Скрипты
    5. ioBroker скрипты
    6. Построение графиков ChartJS на сервере и отправка картинки в телеграм

    NEWS

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    Построение графиков ChartJS на сервере и отправка картинки в телеграм

    This topic has been deleted. Only users with topic management privileges can see it.
    • goofyk
      goofyk last edited by

      в нем нет ошибок 😞 странно, что он текст функции вываливает в лог… у меня только sendTo
      3371__________________2018-10-17_12-57-12.png

      может инстанс history не 0?

      1 Reply Last reply Reply Quote 1
      • goofyk
        goofyk last edited by

        Какая версия Nodejs?

        1 Reply Last reply Reply Quote 1
        • T
          TechElCo last edited by

          Хистори.0… Не знаю, как вставить картинку 😞 Ноде 8.12.0

          Щас код такой:

          `'use strict';
          const ChartjsNode = require('chartjs-node');
          
          /*** Вспомогательные функции (взяты из js-controller) ***/
          
          /**
           * Puts all values from an `arguments` object into an array, starting at the given index
           * @param {IArguments} argsObj An `arguments` object as passed to a function
           * @param {number} [startIndex=0] The optional index to start taking the arguments from
           */
          function sliceArgs(argsObj, startIndex) {
              if (startIndex === null) startIndex = 0;
              const ret = [];
              for (let i = startIndex; i < argsObj.length; i++) {
                  ret.push(argsObj[i]);
              }
              return ret;
          }
          
          /**
           * Promisifies a function which does not provide an error as the first argument in its callback
           * @param {Function} fn The function to promisify
           * @param {any} [context] (optional) The context (value of `this` to bind the function to)
           * @param {string[]} [returnArgNames] (optional) If the callback contains multiple arguments, 
           * you can combine them into one object by passing the names as an array. 
           * Otherwise the Promise will resolve with an array
           * @returns {(...args: any[]) => Promise<any>}
           */
          function promisifyNoError(fn, context, returnArgNames) {
              return function () {
                  const args = sliceArgs(arguments);
                  context = context || this;
                  return new Promise(function (resolve, reject) {
                      fn.apply(context, args.concat([
                          function (result) {
                              // decide on how we want to return the callback arguments
                              switch (arguments.length) {
                                  case 0: // no arguments were given
                                      return resolve(); // Promise <void>case 1: // a single value (result) was returned
                                      return resolve(result);
                                  default: // multiple values should be returned
                                      /** @type {{} | any[]} */
                                      let ret;
                                      const extraArgs = sliceArgs(arguments, 0);
                                      if (returnArgNames && returnArgNames.length === extraArgs.length) {
                                          // we can build an object
                                          ret = {};
                                          for (let i = 0; i < returnArgNames.length; i++) {
                                              ret[returnArgNames[i]] = extraArgs[i];
                                          }
                                      } else {
                                          // we return the raw array
                                          ret = extraArgs;
                                      }
                                      return resolve(ret);
                              }
                          }
                      ]));
                  });
              };
          }
          
          /**
           * функция sendTo как Promise, чтобы удобно было строить цепочки
          */
          var sendToPromise = promisifyNoError(sendTo);
          
          // константы для цветов
          const chartColors = {
              black: 'rgb(0, 0, 0)',
          	red: 'rgb(255, 99, 132)',
          	orange: 'rgb(255, 159, 64)',
          	yellow: 'rgb(255, 205, 86)',
          	green: 'rgb(75, 220, 150)',
          	blue: 'rgb(54, 162, 235)',
          	purple: 'rgb(153, 102, 255)',
          	grey: 'rgb(201, 203, 207)'
          };
          
          /**
           * функция рисования и сохранения картинки в файл
           *  параметры:
           *  @param config - конфигурация графика для рисования
           *  @param filename - имя файла для сохранения
           *  результат:
           *  @param Promise - успешное сохранение файла
           */
          
          function doDraw(config, filename) {
              // создадим полотно с размером 640x480 пикселей
              var chartNode = new ChartjsNode(640, 480);
              return chartNode.drawChart(config)
                  .then(() => {
                      // запишем результат в файл
                      return chartNode.writeImageToFile('image/png', filename);
                  });
          }
          
          /**
           * функция подготовки параметров для ChartJS.
           *  результат:
           *  @param Promise - успешная подготовка параметров
           */
          
          function prepareDraw0(hours){
              // вычислим интервал времени, за который надо получить данные
              const end = new Date().getTime(),
                    start = end - 3600000*(hours || 1); // 1 = час назад
          
              // переменная, куда сохраним данные
              var пример;
              // создадим Promise сборки данных и конфигурации
              return new Promise((resolve, reject)=>{resolve()})
                  // здесь могут быть много шагов сбора данных, прежде чем перейти к графику
                  .then(()=>{
                      return sendToPromise('history.0', 'getHistory', {
                              id: 'mqtt.0.Borovoe1.bmet',
                              options: {
                                  start: start,
                                  end: end,
                                  aggregate: 'onchange'
                          }
                      })
                      .then((result) => {
                          // записываем результат в переменную 'пример'
                          пример = result.result;
                      });
                  })
          
             // финальный шаг - создаем конфигурацию графиков
                  .then(()=>{
                      const chartJsOptions = {
                          // тип графика - линейный
                          type: 'line',
              			data: {
              			    // список наборов данных
              				datasets: [
              			    {
              			        // заголовок ряда 
              					label: 'проба',
              					// цвет
              					backgroundColor: chartColors.green,
              					borderColor: chartColors.green,
              					// размер точек
              					pointRadius: 3,
              					// ширина линии графика
              					borderWidth: 3,
              					// достанем данные из переменной 'пример' и оставим только значение и время изменения
              					data: пример.map((item) => {
              					    return {y: item.val, t: new Date(item.ts)}
              					}),
              					// заливка графика - нет
              					fill: false,
              			    }
              				]
              			},
              			options: {
              				// настройка легенды
              				legend: {
              				    labels: {
              				        // размер шрифта
              				        fontSize: 24,
              				    },
              				},
              				// оси координат
              				scales: {
              				    // оси X
              					xAxes: [{
              					    // тип - временная ось
              					    type: 'time',  
              						display: true,
              						// метка оси
              						scaleLabel: {
              							display: true,
              							labelString: 'Время'
              						},
              					}],
              					// оси Y
              					yAxes: [{
              					    // тип - линейная
              					    type: 'linear',
              						display: true,
              						// метка оси
              						scaleLabel: {
              							display: true,
              							labelString: 'Температура'
              						},
              					}]
              				}
              			}
          			};
          			return chartJsOptions;
                  });
          }
          
          /**
           * функция отправки графика в телеграм
           * @param user - какому юзеру слать. если пусто - всем
           * @param chat_id - 
           * @param message_id - в каком чате и какое сообщение заменить при обновлении
           * @param hours - количество часов, за которые получить данные
           */
          
          //**************************************************************************************
          function sendGraph0(user, chat_id, message_id, hours){
              // имя файла, в который положим картинку с графиком
              const filename = '/tmp/graph0.png';
              hours = hours || 1;
              // выполним подготовку данных 
              prepareDraw0(hours)
                  // на след шаге нарисуем
                  .then((result) => {
                      // рисуем картинку по полученным данным и конфигурации
                      return doDraw(result, filename);
                  })
                 .then(() => {
                      // удалим предыдущее сообщение
                      if (message_id && chat_id) {
                          sendTo('telegram.0', {
                              user: user,
                              deleteMessage: {
                                  options: {
                                      chat_id: chat_id, 
                                      message_id: message_id
                                  }
                              }
                          });
                      }
                  })
                  .then(()=>{
                      // теперь отправим сообщение в телеграм
                      sendTo('telegram.0', {
                          user: user, 
                          text: filename, 
                          caption: 'Температура в курятниках ('+hours+'ч)',
                          reply_markup: {
                              inline_keyboard: [
                                  [
                                      { text: '🔄', callback_data: 'graph_'+hours},
                                      { text: '1 ч', callback_data: 'graph_1' },
                                      { text: '2 ч', callback_data: 'graph_2' },
                                      { text: '4 ч', callback_data: 'graph_4' },
                                      { text: '12 ч', callback_data: 'graph_12' },
                                      { text: '24 ч', callback_data: 'graph_24' },
                                  ]
                              ]
                          }
                      });
                  });
          }
          
          // будем слушать телеграм и ждать команды на построение графика
          on({id: "telegram.0.communicate.request", ack: false, change: 'any'}, function (obj) {
              var v;
              var msg = obj.state.val;
              var command = obj.state.val.substring(obj.state.val.indexOf(']')+1);
              var user = obj.state.val.substring(obj.state.val.indexOf('[')+1,obj.state.val.indexOf(']'));
              var chat_id = getState("telegram.0.communicate.requestChatId").val;
              var message_id = getState("telegram.0.communicate.requestMessageId").val;
          
              // команда для графика - demo
              if (command.startsWith('demo')) {
                  const hours = parseInt(command.split('_')[1]);
                  sendGraph0(user, chat_id, message_id, hours);
              }
          });</void></any>`[/i][/i]
          
          1 Reply Last reply Reply Quote 0
          • goofyk
            goofyk last edited by

            Попробуй вот такую функцию, вместо той, что в скрипте

            function sendToPromise(adaper, cmd, params) {
                return new Promise((resolve, reject)=>{
                    sendTo(adaper, cmd, params, (result) => {
                        resolve(result);
                    });
                });
            }
            
            

            Обновил скрипт в первом посте, убрал оттуда вспомогательные функции и вставил эту функцию.

            1 Reply Last reply Reply Quote 1
            • T
              TechElCo last edited by

              Так сработало, но до этого на месте этой функции было:

              /**
               * функция sendTo как Promise, чтобы удобно было строить цепочки
              */
              var sendToPromise = promisifyNoError(sendTo);
              

              Я думал, что тут что-то криво скопировалось…

              1 Reply Last reply Reply Quote 0
              • goofyk
                goofyk last edited by

                Уфф, разобрались.

                1 Reply Last reply Reply Quote 1
                • T
                  TechElCo last edited by

                  Я, честно говоря, пока не разобрался 8-) Но мне ещё надо покурить много, а сегодня я урывками, на ходу, то с работы, то из дома пробовал код менять. Вечерком по изучаю, спасибо за помощь 😉 Но вообще, у меня пока отдаёт картинку только за час, один раз, как то случайно отдал за 24 часа… Вот с таким логом : ` > telegram.0 2018-10-17 15:03:36.149 error Cannot send deleteMessage [chatId - 289938044]: Error: ETELEGRAM: 400 Bad Request: message can't be deleted

                  javascript.1 2018-10-17 15:03:35.561 debug sendTo "send" to system.adapter.telegram.0 from system.adapter.javascript.1

                  javascript.1 2018-10-17 15:03:35.560 info script.js.test.Скрипт3: sendTo(adapter=telegram.0, cmd=[object Object], msg=undefined)

                  javascript.1 2018-10-17 15:03:35.559 debug sendTo "send" to system.adapter.telegram.0 from system.adapter.javascript.1

                  javascript.1 2018-10-17 15:03:35.558 info script.js.test.Скрипт3: sendTo(adapter=telegram.0, cmd=[object Object], msg=undefined)

                  javascript.1 2018-10-17 15:03:35.254 debug sendTo "getHistory" to system.adapter.history.0 from system.adapter.javascript.1

                  javascript.1 2018-10-17 15:03:35.253 info script.js.test.Скрипт3: sendTo(adapter=history.0, cmd=getHistory, msg={"id":"mqtt.0.Borovoe1.bmet","options":{"start":1539774215252,"end":1539777815252,"aggregate":"onchange"}})

                  javascript.1 2018-10-17 15:03:35.251 info script.js.test.Скрипт3: getState(id=telegram.0.communicate.requestMessageId, timerId=0) => {"val":19327,"ack":false,"ts":1539777815225,"q":0,"from":"system.adapter.telegram.0","lc":1539777815225}

                  javascript.1 2018-10-17 15:03:35.249 info script.js.test.Скрипт3: getState(id=telegram.0.communicate.requestChatId, timerId=0) => {"val":289938044,"ack":false,"ts":1539777815213,"q":0,"from":"system.adapter.telegram.0","lc":1538911049076}

                  telegram.0 2018-10-17 15:03:31.405 error Cannot send deleteMessage [chatId - 289938044]: Error: ETELEGRAM: 400 Bad Request: message can't be deleted `

                  1 Reply Last reply Reply Quote 0
                  • T
                    TechElCo last edited by

                    @goofyk:

                    Попробуй вот такую функцию, вместо той, что в скрипте

                    function sendToPromise(adaper, cmd, params) {
                        return new Promise((resolve, reject)=>{
                            sendTo(adaper, cmd, params, (result) => {
                                resolve(result);
                            });
                        });
                    }
                    
                    

                    Обновил скрипт в первом посте, убрал оттуда вспомогательные функции и вставил эту функцию. `

                    Поменял adaper на adapter, стало лучше )

                    1 Reply Last reply Reply Quote 0
                    • goofyk
                      goofyk last edited by

                      @TechElCo:

                      Поменял adaper на adapter, стало лучше ) `

                      :)) Спасибо

                      1 Reply Last reply Reply Quote 0
                      • S
                        Sergey777 last edited by

                        А можно так, чтоб не на сервере графики строились? А как обычно, в браузере. Но как chartJS тогда совместить с histori драйвером?

                        1 Reply Last reply Reply Quote 0
                        • K
                          kristow @goofyk last edited by

                          @goofyk
                          Добрый день.
                          Установил chartjs и chartjs-node.
                          На основе вашего примера прописал свои параметры для вывода в график. Когда запускаю скрипт, то ошибок нет. Когда отправляю в телеграмм "demo", то вылетает ошибка "script.js.common.Chart: TypeError: canvas.getRootNode is not a function" и на этом все... Можете подсказать что сделать? для решения этой проблемы?
                          В самом скрипте этой функции нет. Нужно как-то хитрее ставить библиотеки? У меня iobroker работает на rasbian (если имеет значение).

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post

                          Support us

                          ioBroker
                          Community Adapters
                          Donate

                          429
                          Online

                          31.8k
                          Users

                          80.0k
                          Topics

                          1.3m
                          Posts

                          4
                          22
                          6582
                          Loading More Posts
                          • Oldest to Newest
                          • Newest to Oldest
                          • Most Votes
                          Reply
                          • Reply as topic
                          Log in to reply
                          Community
                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                          The ioBroker Community 2014-2023
                          logo