Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Русский
  3. ioBroker
  4. Скрипты
  5. ioBroker скрипты
  6. Построение графиков ChartJS на сервере и отправка картинки в телеграм

NEWS

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    10
    1
    122

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    24
    1
    1.4k

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    9.3k

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

Geplant Angeheftet Gesperrt Verschoben ioBroker скрипты
22 Beiträge 4 Kommentatoren 7.4k Aufrufe 1 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • T Offline
    T Offline
    TechElCo
    schrieb am zuletzt editiert von
    #4
    /**
     * функция sendTo как Promise, чтобы удобно было строить цепочки
     */
    
    var sendToPromise = promisifyNoError(sendTo);
    
    ````А тут никакой ошибки нет? Я ставлю свои пять состояний, которые есть в хистори, но это ничего не меняет ((
    
    Вот такое в логе скрипта после приёма команды граф:  ` > javascript.1 script.js.test.Скрипт1: sendTo(adapter=function (result) { // decide on how we want to return the callback arguments switch (arguments.length) { case 0: // no arguments were given return resolve(); // Promise 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__; } } else { // we return the raw array ret = extraArgs; } return resolve(ret); } }, cmd=undefined, msg=undefined)___ ` 
    1 Antwort Letzte Antwort
    0
    • goofykG Offline
      goofykG Offline
      goofyk
      schrieb am zuletzt editiert von
      #5

      Ошибки быть не должно - это оборачивание функции sendTo в промис.

      Выше должны быть 2 вспомогательные функции для этого (promisifyNoError и sliceArgs).

      Можно переписать скрипт, чтобы использовалась обычная функция sendTo, но тогда надо в callback передавать следующую функцию и т.д.

      1 Antwort Letzte Antwort
      1
      • goofykG Offline
        goofykG Offline
        goofyk
        schrieb am zuletzt editiert von
        #6

        Покажите какая у вас получилась функция prepareDraw?

        1 Antwort Letzte Antwort
        1
        • T Offline
          T Offline
          TechElCo
          schrieb am zuletzt editiert von
          #7
          /**
           * функция подготовки параметров для ChartJS.
           * собирает данные из истории и складывает их в переменные, 
           * чтобы потом включить в ряды.
           * 
           *  параметры:
           *  @param hours - количество часов, за которые получить данные
           *  результат:
           *  @param Promise - успешная подготовка параметров
           */
          
          function prepareDraw1(hours){
              // вычислим интервал времени, за который надо получить данные
              const end = new Date().getTime(),
                    start = end - 3600000*(hours || 1); // 1 = час назад
          
              // зададим переменные, в которые будем складывать результаты запроса
              // исторических данных
              var улица, куры2, куры1, куры2свет, куры2вент;
          
              // создадим Promise сборки данных и конфигурации
              return new Promise((resolve, reject)=>{resolve()})
          
                  // на этом шаге собираем историю по 'mqtt.0.Borovoe1.bmet'
                  .then(() => {
                      return sendToPromise('history.0', 'getHistory', {
                              id: 'mqtt.0.Borovoe1.bmet',
                              options: {
                                  start: start,
                                  end: end,
                                  aggregate: 'onchange'
                          }
                      })
                      .then((result) => {
                          // записываем результат в переменную 'улица'
                          улица = result.result;
                      });
                  })
          
                  // на этом шаге собираем историю по 'sonoff.0.chicken2.DS18B20_Temperature'
                  .then(() => {
                      return sendToPromise('history.0', 'getHistory', {
                          id: 'mqtt.0.Sonoff.dsw1',
                          options: {
                              start: start,
                              end: end,
                              aggregate: 'onchange'
                          }
                      }).then((result)=>{
                          // записываем результат в переменную 'куры2'
                          куры2 = result.result;
                      });
                  })
          
                  .then(() => {
                      return sendToPromise('history.0', 'getHistory', {
                          id: 'zigbee.0.00158d0001dbd5d6.temperature',
                          options: {
                              start: start,
                              end: end,
                              aggregate: 'onchange'
                          }
                      }).then((result)=>{
                          куры1 = result.result;
                      });
                  })
          
                  .then(() => {
                      return sendToPromise('history.0', 'getHistory', {
                          id: 'mqtt.0.Sonoff.output12',
                          options: {
                              start: start,
                              end: end,
                              aggregate: 'onchange'
                          }
                      }).then((result)=>{
                          куры2свет = result.result;
                      });
                  })
          
                  .then(() => {
                      return sendToPromise('history.0', 'getHistory', {
                          id: 'parser.0.cpuTemperature',
                          options: {
                              start: start,
                              end: end,
                              aggregate: 'onchange'
                          }
                      }).then((result)=>{
                          куры2вент = result.result;
                      });
                  })
          
                  // финальный шаг - создаем конфигурацию графиков
                  .then(()=>{
                      const chartJsOptions = {
                          // тип графика - линейный
                          type: 'line',
              			data: {
              			    // список наборов данных
              				datasets: [
              			    {
              			        // заголовок ряда с указанием последнего значения из ряда в скобках
              					label: 'Улица ('+улица[улица.length - 1].val+')',
              					// цвет
              					backgroundColor: chartColors.blue,
              					borderColor: chartColors.blue,
              					// размер точек. 0 - нет точки
              					pointRadius: 0,
              					// ширина линии графика
              					borderWidth: 3,
              					// достанем данные из переменной 'улица' и оставим только значение и время изменения
              					data: улица.map((item) => {
              					    return {y: item.val, t: new Date(item.ts)}
              					}),
              					// заливка графика - нет
              					fill: false,
              					// идентификатор оси Y
              					yAxisID: 'y-axis-1',
              			    },{
              					label: 'Куры 1 ('+куры1[куры1.length - 1].val+')',
              					backgroundColor: chartColors.green,
              					borderColor: chartColors.green,
              					pointRadius: 0,
              					borderWidth: 3,
              					data: куры1.map((item) => {
              					    return {y: item.val, t: new Date(item.ts)}
              					}),
              					fill: false,
              					yAxisID: 'y-axis-1',
              				},{
              					label: 'Куры 2 ('+куры2[куры2.length - 1].val+')',
              					backgroundColor: chartColors.red,
              					borderColor: chartColors.red,
              					pointRadius: 0,
              					borderWidth: 3,
              					data: куры2.map((item) => {
              					    return {y: item.val, t: new Date(item.ts)}
              					}),
              					fill: false,
              					yAxisID: 'y-axis-1',
              				},{
              					label: 'Куры 2 свет ('+куры2свет[куры2свет.length - 1].val+')',
              					backgroundColor: chartColors.yellow,
              					borderColor: chartColors.yellow,
              					pointRadius: 0,
              					borderWidth: 1,
              					data: куры2свет.map((item) => {
              					    return {y: (item.val) ? 1 : 0, t: new Date(item.ts)}
              					}),
              					fill: true,
              					lineTension: 0,
          		                steppedLine: true,
              					yAxisID: 'y-axis-2',
              				},{
              					label: 'Куры 2 вент ('+куры2вент[куры2вент.length - 1].val+')',
              					backgroundColor: chartColors.grey,
              					borderColor: chartColors.grey,
              					pointRadius: 0,
              					borderWidth: 1,
              					data: куры2вент.map((item) => {
              					    return {y: (item.val) ? -1 : 0, t: new Date(item.ts)}
              					}),
              					fill: true,
              					lineTension: 0,
          		                steppedLine: true,
              					yAxisID: 'y-axis-2',
              				}
              				]
              			},
              			options: {
              				// настройка легенды
              				legend: {
              				    labels: {
              				        // размер шрифта
              				        fontSize: 20,
              				    },
              				},
              				// оси координат
              				scales: {
              				    // оси X
              					xAxes: [{
              					    // тип - временная ось
              					    type: 'time',  
              						display: true,
              						// метка оси
              						scaleLabel: {
              							display: true,
              							labelString: 'Время'
              						},
              						// настройка формата оси (времени)
              						time: {
              						    unit: 'minute',
              						    displayFormats: {
                                              minute: 'HH:mm'
                                          }
              						},
              					}],
              					// оси Y
              					yAxes: [{
              					    // тип - линейная
              					    type: 'linear',
              						display: true,
              						// метка оси
              						scaleLabel: {
              							display: true,
              							labelString: 'Температура'
              						},
              						// расположение линейки - слева
              						position: 'left',
              						// идентификатор оси
              						id: 'y-axis-1',
              					},{
              					    type: 'linear',
              					    display: true,
              						scaleLabel: {
              							display: true,
              							labelString: 'Свет и вентиляция'
              						},
              						ticks: {
              							min: -4,
              							max: 2
              						},
              						// расположение линейки - справа
              						position: 'right',
              						id: 'y-axis-2',
                  				}]
              				}
              			}
          			};
          			return chartJsOptions;
                  });
          }
          

          Я только подставил пять своих состояний, больше ничего не менял

          1 Antwort Letzte Antwort
          0
          • goofykG Offline
            goofykG Offline
            goofyk
            schrieb am zuletzt editiert von
            #8

            хм. по всем этим состояниям ведется история и она есть на запрашиваемый период?

            тогда надо начать с простого варианта. переделываем пример prepareDraw0 и добавляем туда получение истории одного состояния, вместо заданных значений. сможете это сделать? это позволит понять работает ли получение истории.

            может быть дело в NodeJS или версии контроллера или еще в чем-то…

            1 Antwort Letzte Antwort
            1
            • T Offline
              T Offline
              TechElCo
              schrieb am zuletzt editiert von
              #9

              По всем есть история, буду пробовать переделывать демо…

              1 Antwort Letzte Antwort
              0
              • goofykG Offline
                goofykG Offline
                goofyk
                schrieb am zuletzt editiert von
                #10

                Б-р-р-р… а в логах что?

                Попробуем получить историю без промиса:

                
                function prepareDraw0(hours){
                    // вычислим интервал времени, за который надо получить данные
                    const end = new Date().getTime(),
                          start = end - 3600000*(hours || 1); // 1 = час назад
                    // переменная, куда сохраним данные
                    var пример;
                    // создадим Promise сборки данных и конфигурации
                    return new Promise((resolve, reject)=>{
                            sendTo('history.0', 'getHistory', {
                                    id: 'mqtt.0.Borovoe1.bmet',
                                    options: {
                                        start: start,
                                        end: end,
                                        aggregate: 'onchange'
                                }
                            }, (result) => {
                                пример = result.result;
                                resolve();
                            })
                        })
                        // финальный шаг - создаем конфигурацию графиков
                        .then(()=>{
                
                
                1 Antwort Letzte Antwort
                1
                • T Offline
                  T Offline
                  TechElCo
                  schrieb am zuletzt editiert von
                  #11

                  ` > javascript.1 2018-10-17 12:47:21.360 info }, cmd=undefined, msg=undefined)

                  javascript.1 2018-10-17 12:47:21.360 info }

                  javascript.1 2018-10-17 12:47:21.360 info return resolve(ret);

                  javascript.1 2018-10-17 12:47:21.360 info }

                  javascript.1 2018-10-17 12:47:21.360 info ret = extraArgs;

                  javascript.1 2018-10-17 12:47:21.360 info // we return the raw array

                  javascript.1 2018-10-17 12:47:21.360 info } else {

                  javascript.1 2018-10-17 12:47:21.360 info }

                  javascript.1 2018-10-17 12:47:21.360 info ret[returnArgNames_[i]] = extraArgs__;

                  javascript.1 2018-10-17 12:47:21.360 info for (let i = 0; i < returnArgNames.length; i++) {

                  javascript.1 2018-10-17 12:47:21.360 info ret = {};

                  javascript.1 2018-10-17 12:47:21.360 info // we can build an object

                  javascript.1 2018-10-17 12:47:21.360 info if (returnArgNames && returnArgNames.length === extraArgs.length) {

                  javascript.1 2018-10-17 12:47:21.360 info const extraArgs = sliceArgs(arguments, 0);

                  javascript.1 2018-10-17 12:47:21.360 info let ret;

                  javascript.1 2018-10-17 12:47:21.360 info /** @type {{} | any[]} */

                  javascript.1 2018-10-17 12:47:21.360 info default: // multiple values should be returned

                  javascript.1 2018-10-17 12:47:21.360 info return resolve(result);

                  javascript.1 2018-10-17 12:47:21.360 info case 1: // a single value (result) was returned

                  javascript.1 2018-10-17 12:47:21.360 info return resolve(); // Promise <void>javascript.1 2018-10-17 12:47:21.360 info case 0: // no arguments were given

                  javascript.1 2018-10-17 12:47:21.360 info switch (arguments.length) {

                  javascript.1 2018-10-17 12:47:21.360 info // decide on how we want to return the callback arguments

                  javascript.1 2018-10-17 12:47:21.360 info script.js.test.Скрипт2: sendTo(adapter=function (result) {

                  javascript.1 2018-10-17 12:47:21.357 info script.js.test.Скрипт2: getState(id=telegram.0.communicate.requestMessageId, timerId=0) => {"val":19302,"ack":false,"ts":1539769641330,"q":0,"from":"system.adapter.telegram.0","lc":1539769641330}

                  javascript.1 2018-10-17 12:47:21.355 info script.js.test.Скрипт2: getState(id=telegram.0.communicate.requestChatId, timerId=0) => {"val":289938044,"ack":false,"ts":1539769641319,"q":0,"from":"system.adapter.telegram.0","lc":1538911049076}</void>___ Отвлекли, вот лог, это ещё с промисом. Что из него можно узнать? `

                  1 Antwort Letzte Antwort
                  0
                  • goofykG Offline
                    goofykG Offline
                    goofyk
                    schrieb am zuletzt editiert von
                    #12

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

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

                    1 Antwort Letzte Antwort
                    1
                    • goofykG Offline
                      goofykG Offline
                      goofyk
                      schrieb am zuletzt editiert von
                      #13

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

                      1 Antwort Letzte Antwort
                      1
                      • T Offline
                        T Offline
                        TechElCo
                        schrieb am zuletzt editiert von
                        #14

                        Хистори.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 Antwort Letzte Antwort
                        0
                        • goofykG Offline
                          goofykG Offline
                          goofyk
                          schrieb am zuletzt editiert von
                          #15

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

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

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

                          1 Antwort Letzte Antwort
                          1
                          • T Offline
                            T Offline
                            TechElCo
                            schrieb am zuletzt editiert von
                            #16

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

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

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

                            1 Antwort Letzte Antwort
                            0
                            • goofykG Offline
                              goofykG Offline
                              goofyk
                              schrieb am zuletzt editiert von
                              #17

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

                              1 Antwort Letzte Antwort
                              1
                              • T Offline
                                T Offline
                                TechElCo
                                schrieb am zuletzt editiert von
                                #18

                                Я, честно говоря, пока не разобрался 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 Antwort Letzte Antwort
                                0
                                • T Offline
                                  T Offline
                                  TechElCo
                                  schrieb am zuletzt editiert von
                                  #19

                                  @goofyk:

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

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

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

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

                                  1 Antwort Letzte Antwort
                                  0
                                  • goofykG Offline
                                    goofykG Offline
                                    goofyk
                                    schrieb am zuletzt editiert von
                                    #20

                                    @TechElCo:

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

                                    :)) Спасибо

                                    1 Antwort Letzte Antwort
                                    0
                                    • S Offline
                                      S Offline
                                      Sergey777
                                      schrieb am zuletzt editiert von
                                      #21

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

                                      1 Antwort Letzte Antwort
                                      0
                                      • goofykG goofyk

                                        Расскажу, как можно получить график в виде картинки, например для отправки в Telegram

                                        В ioBroker есть стандартный способ построения графиков - Flot-драйвер. Этот драйвер работает в паре с Web-драйвером и отображает результат в браузере. Но для того чтобы получить созданный график на сервере (в скрипте) в виде картинки нужен дополнительный драйвер PhantomJS, который делает “скриншот” страницы (на которой у нас нарисуется Flot-график).

                                        Но я расскажу об альтернативном способе построения графиков на сервере в скрипте.

                                        Есть такая библиотека Chart.js http://www.chartjs.org/ которая позволяет рисовать приятные на вид графики в браузере (примеры http://www.chartjs.org/samples/latest/).

                                        Для рисования она использует “холст” (канва, canvas) браузера. Поэтому, чтобы рисовать с помощью этой библиотеки на сервере, нужно использовать “серверный” вариант “холста” и DOM-объекты. Это и делает пакет chartjs-node (https://github.com/vmpowerio/chartjs-node).

                                        Основной зависимостью для этого пакета является пакет canvas (https://github.com/Automattic/node-canvas), который следует установить глобально (или в папку iobroker). Важно установить все зависимости для той платформы, куда вы ставите https://github.com/Automattic/node-canvas#compiling .

                                        После этого можно в настройках драйвера javascript добавить модули chart.js, chartjs-node. Они должны установиться корректно, без ошибок. Иначе - разбираться с ошибками и решать их.

                                        А дальше, можно написать скрипт.

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

                                        Внимание! В скрипте есть сложные для новичков конструкции - Promise. Это удобные способ не писать функции с callback, а делать цепочки шагов. Так, например, это удобно делать для получения данных из истории состояний.

                                        Скрипт подписывается на 2 команды, приходящие через Телеграм:

                                        demo - строит график на готовых тестовых данных (чтобы проверить, что код вообще работает). Вызываются функции sendGraph0, prepareDraw0, doDraw.
                                        3371__________________2018-10-16_16-08-30.png

                                        graph - строит график на данных из истории состояний. Вызываются функции sendGraph1, prepareDraw1, doDraw.
                                        3371__________________2018-10-16_16-47-02.png

                                        
                                        'use strict';
                                        const ChartjsNode = require('chartjs-node');
                                        
                                        /**
                                         * функция sendTo как Promise, чтобы удобно было строить цепочки
                                         */
                                        
                                        function sendToPromise(adapter, cmd, params) {
                                            return new Promise((resolve, reject) => {
                                                sendTo(adapter, cmd, params, (result) => {
                                                    resolve(result);
                                                });
                                            });
                                        }
                                        
                                        // константы для цветов
                                        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, 192, 192)',
                                        	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(){
                                            // переменная, куда сохраним данные
                                            var пример;
                                            // создадим Promise сборки данных и конфигурации
                                            return new Promise((resolve, reject)=>{resolve()})
                                                // здесь могут быть много шагов сбора данных, прежде чем перейти к графику
                                                .then(()=>{
                                                    // произвольные данные, похожие на те, что хранятся в истории
                                                    пример = [
                                                        {"val":3,"ack":1,"ts":1539063874301},
                                                        {"val":5,"ack":1,"ts":1539063884299},
                                                        {"val":5.3,"ack":1,"ts":1539063894299},
                                                        {"val":3.39,"ack":1,"ts":1539063904301},
                                                        {"val":5.6,"ack":1,"ts":1539063914300},
                                                        {"val":-1.3,"ack":1,"ts":1539063924300},
                                                        {"val":-6.3,"ack":1,"ts":1539063934302},
                                                        {"val":1.23,"ack":1,"ts":1539063944301},
                                                    ];
                                                })
                                                // финальный шаг - создаем конфигурацию графиков
                                                .then(()=>{
                                                    const chartJsOptions = {
                                                        // тип графика - линейный
                                                        type: 'line',
                                            			data: {
                                            			    // список наборов данных
                                            				datasets: [
                                            			    {
                                            			        // заголовок ряда 
                                            					label: 'тест',
                                            					// цвет
                                            					backgroundColor: chartColors.black,
                                            					borderColor: chartColors.black,
                                            					// размер точек
                                            					pointRadius: 3,
                                            					// ширина линии графика
                                            					borderWidth: 3,
                                            					// достанем данные из переменной 'пример' и оставим только значение и время изменения
                                            					data: пример.map((item) => {
                                            					    return {y: item.val, t: new Date(item.ts)}
                                            					}),
                                            					// заливка графика - нет
                                            					fill: false,
                                            			    }
                                            				]
                                            			},
                                            			options: {
                                            				// настройка легенды
                                            				legend: {
                                            				    labels: {
                                            				        // размер шрифта
                                            				        fontSize: 20,
                                            				    },
                                            				},
                                            				// оси координат
                                            				scales: {
                                            				    // оси X
                                            					xAxes: [{
                                            					    // тип - временная ось
                                            					    type: 'time',  
                                            						display: true,
                                            						// метка оси
                                            						scaleLabel: {
                                            							display: true,
                                            							labelString: 'Время'
                                            						},
                                            					}],
                                            					// оси Y
                                            					yAxes: [{
                                            					    // тип - линейная
                                            					    type: 'linear',
                                            						display: true,
                                            						// метка оси
                                            						scaleLabel: {
                                            							display: true,
                                            							labelString: 'Температура'
                                            						},
                                            					}]
                                            				}
                                            			}
                                        			};
                                        			return chartJsOptions;
                                                });
                                        }
                                        
                                        /**
                                         * функция подготовки параметров для ChartJS.
                                         * собирает данные из истории и складывает их в переменные, 
                                         * чтобы потом включить в ряды.
                                         * 
                                         *  параметры:
                                         *  @param hours - количество часов, за которые получить данные
                                         *  результат:
                                         *  @param Promise - успешная подготовка параметров
                                         */
                                        
                                        function prepareDraw1(hours){
                                            // вычислим интервал времени, за который надо получить данные
                                            const end = new Date().getTime(),
                                                  start = end - 3600000*(hours || 1); // 1 = час назад
                                        
                                            // зададим переменные, в которые будем складывать результаты запроса
                                            // исторических данных
                                            var улица, куры2, куры1, куры2свет, куры2вент;
                                        
                                            // создадим Promise сборки данных и конфигурации
                                            return new Promise((resolve, reject)=>{resolve()})
                                                // на этом шаге собираем историю по 'mqtt.0.ESP_Easy.Улица.Temperature'
                                                .then(() => {
                                                    return sendToPromise('history.0', 'getHistory', {
                                                            id: 'mqtt.0.ESP_Easy.Улица.Temperature',
                                                            options: {
                                                                start: start,
                                                                end: end,
                                                                aggregate: 'onchange'
                                                            }
                                                        }
                                                    ).then((result) => {
                                                        // записываем результат в переменную 'улица'
                                                        улица = result.result;
                                                    });
                                                })
                                                // на этом шаге собираем историю по 'sonoff.0.chicken2.DS18B20_Temperature'
                                                .then(() => {
                                                    return sendToPromise('history.0', 'getHistory', {
                                                        id: 'sonoff.0.chicken2.DS18B20_Temperature',
                                                        options: {
                                                            start: start,
                                                            end: end,
                                                            aggregate: 'onchange'
                                                        }
                                                    }).then((result)=>{
                                                        // записываем результат в переменную 'куры2'
                                                        куры2 = result.result;
                                                    });
                                                })
                                                .then(() => {
                                                    return sendToPromise('history.0', 'getHistory', {
                                                        id: 'sonoff.0.sonoff_chicken_vent.DS18B20_Temperature',
                                                        options: {
                                                            start: start,
                                                            end: end,
                                                            aggregate: 'onchange'
                                                        }
                                                    }).then((result)=>{
                                                        куры1 = result.result;
                                                    });
                                                })
                                                .then(() => {
                                                    return sendToPromise('history.0', 'getHistory', {
                                                        id: 'sonoff.0.chicken2.POWER1',
                                                        options: {
                                                            start: start,
                                                            end: end,
                                                            aggregate: 'onchange'
                                                        }
                                                    }).then((result)=>{
                                                        куры2свет = result.result;
                                                    });
                                                })
                                                .then(() => {
                                                    return sendToPromise('history.0', 'getHistory', {
                                                        id: 'sonoff.0.chicken2.POWER2',
                                                        options: {
                                                            start: start,
                                                            end: end,
                                                            aggregate: 'onchange'
                                                        }
                                                    }).then((result)=>{
                                                        куры2вент = result.result;
                                                    });
                                                })
                                                // финальный шаг - создаем конфигурацию графиков
                                                .then(()=>{
                                                    const chartJsOptions = {
                                                        // тип графика - линейный
                                                        type: 'line',
                                            			data: {
                                            			    // список наборов данных
                                            				datasets: [
                                            			    {
                                            			        // заголовок ряда с указанием последнего значения из ряда в скобках
                                            					label: 'Улица ('+улица[улица.length - 1].val+')',
                                            					// цвет
                                            					backgroundColor: chartColors.blue,
                                            					borderColor: chartColors.blue,
                                            					// размер точек. 0 - нет точки
                                            					pointRadius: 0,
                                            					// ширина линии графика
                                            					borderWidth: 3,
                                            					// достанем данные из переменной 'улица' и оставим только значение и время изменения
                                            					data: улица.map((item) => {
                                            					    return {y: item.val, t: new Date(item.ts)}
                                            					}),
                                            					// заливка графика - нет
                                            					fill: false,
                                            					// идентификатор оси Y
                                            					yAxisID: 'y-axis-1',
                                            			    },{
                                            					label: 'Куры 1 ('+куры1[куры1.length - 1].val+')',
                                            					backgroundColor: chartColors.green,
                                            					borderColor: chartColors.green,
                                            					pointRadius: 0,
                                            					borderWidth: 3,
                                            					data: куры1.map((item) => {
                                            					    return {y: item.val, t: new Date(item.ts)}
                                            					}),
                                            					fill: false,
                                            					yAxisID: 'y-axis-1',
                                            				},{
                                            					label: 'Куры 2 ('+куры2[куры2.length - 1].val+')',
                                            					backgroundColor: chartColors.red,
                                            					borderColor: chartColors.red,
                                            					pointRadius: 0,
                                            					borderWidth: 3,
                                            					data: куры2.map((item) => {
                                            					    return {y: item.val, t: new Date(item.ts)}
                                            					}),
                                            					fill: false,
                                            					yAxisID: 'y-axis-1',
                                            				},{
                                            					label: 'Куры 2 свет ('+куры2свет[куры2свет.length - 1].val+')',
                                            					backgroundColor: chartColors.yellow,
                                            					borderColor: chartColors.yellow,
                                            					pointRadius: 0,
                                            					borderWidth: 1,
                                            					data: куры2свет.map((item) => {
                                            					    return {y: (item.val) ? 1 : 0, t: new Date(item.ts)}
                                            					}),
                                            					fill: true,
                                            					lineTension: 0,
                                        		                steppedLine: true,
                                            					yAxisID: 'y-axis-2',
                                            				},{
                                            					label: 'Куры 2 вент ('+куры2вент[куры2вент.length - 1].val+')',
                                            					backgroundColor: chartColors.grey,
                                            					borderColor: chartColors.grey,
                                            					pointRadius: 0,
                                            					borderWidth: 1,
                                            					data: куры2вент.map((item) => {
                                            					    return {y: (item.val) ? -1 : 0, t: new Date(item.ts)}
                                            					}),
                                            					fill: true,
                                            					lineTension: 0,
                                        		                steppedLine: true,
                                            					yAxisID: 'y-axis-2',
                                            				}
                                            				]
                                            			},
                                            			options: {
                                            				// настройка легенды
                                            				legend: {
                                            				    labels: {
                                            				        // размер шрифта
                                            				        fontSize: 20,
                                            				    },
                                            				},
                                            				// оси координат
                                            				scales: {
                                            				    // оси X
                                            					xAxes: [{
                                            					    // тип - временная ось
                                            					    type: 'time',  
                                            						display: true,
                                            						// метка оси
                                            						scaleLabel: {
                                            							display: true,
                                            							labelString: 'Время'
                                            						},
                                            						// настройка формата оси (времени)
                                            						time: {
                                            						    unit: 'minute',
                                            						    displayFormats: {
                                                                            minute: 'HH:mm'
                                                                        }
                                            						},
                                            					}],
                                            					// оси Y
                                            					yAxes: [{
                                            					    // тип - линейная
                                            					    type: 'linear',
                                            						display: true,
                                            						// метка оси
                                            						scaleLabel: {
                                            							display: true,
                                            							labelString: 'Температура'
                                            						},
                                            						// расположение линейки - слева
                                            						position: 'left',
                                            						// идентификатор оси
                                            						id: 'y-axis-1',
                                            					},{
                                            					    type: 'linear',
                                            					    display: true,
                                            						scaleLabel: {
                                            							display: true,
                                            							labelString: 'Свет и вентиляция'
                                            						},
                                            						ticks: {
                                            							min: -4,
                                            							max: 2
                                            						},
                                            						// расположение линейки - справа
                                            						position: 'right',
                                            						id: 'y-axis-2',
                                                				}]
                                            				}
                                            			}
                                        			};
                                        			return chartJsOptions;
                                                });
                                        }
                                        
                                        function sendGraph0(user){
                                            // имя файла, в который положим картинку с графиком
                                            const filename = '/tmp/graph0.png';
                                            // выполним подготовку данных 
                                            prepareDraw0()
                                                // на след шаге нарисуем
                                                .then((result) => {
                                                    // рисуем картинку по полученным данным и конфигурации
                                                    return doDraw(result, filename);
                                                })
                                                .then(()=>{
                                                    // теперь отправим сообщение в телеграм
                                                    sendTo('telegram.0', {
                                                        user: user, 
                                                        text: filename, 
                                                        caption: 'Пример графика',
                                                    });
                                                })
                                                .catch((err)=>{
                                                    console.error(err);
                                                });
                                        }
                                        
                                        /**
                                         * функция отправки графика в телеграм
                                         * @param user - какому юзеру слать. если пусто - всем
                                         * @param chat_id - 
                                         * @param message_id - в каком чате и какое сообщение заменить при обновлении
                                         * @param hours - количество часов, за которые получить данные
                                         */
                                        
                                        function sendGraph1(user, chat_id, message_id, hours){
                                            // имя файла, в который положим картинку с графиком
                                            const filename = '/tmp/graph1.png';
                                            hours = hours || 1;
                                            // выполним подготовку данных 
                                            prepareDraw1(hours)
                                                // на след шаге нарисуем
                                                .then((result) => {
                                                    // рисуем картинку по полученным данным и конфигурации
                                                    return doDraw(result, filename);
                                                })
                                                .then(() => {
                                                    // удалим предыдущее сообщение
                                                    if (message_id && chat_id) {
                                                        sendTo('telegram', {
                                                            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' },
                                                                ]
                                                            ]
                                                        }
                                                    });
                                                })
                                                .catch((err)=>{
                                                    console.error(err);
                                                });
                                        }
                                        
                                        // будем слушать телеграм и ждать команды на построение графика
                                        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 == 'demo') {
                                                sendGraph0(user);
                                            }
                                            // команда для графика - graph
                                            if (command.startsWith('graph')) {
                                                const hours = parseInt(command.split('_')[1]);
                                                sendGraph1(user, chat_id, message_id, hours);
                                            }
                                        });
                                        
                                        
                                        K Offline
                                        K Offline
                                        kristow
                                        schrieb am zuletzt editiert von
                                        #22

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

                                        1 Antwort Letzte Antwort
                                        0
                                        Antworten
                                        • In einem neuen Thema antworten
                                        Anmelden zum Antworten
                                        • Älteste zuerst
                                        • Neuste zuerst
                                        • Meiste Stimmen


                                        Support us

                                        ioBroker
                                        Community Adapters
                                        Donate

                                        429

                                        Online

                                        32.5k

                                        Benutzer

                                        81.7k

                                        Themen

                                        1.3m

                                        Beiträge
                                        Community
                                        Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                        ioBroker Community 2014-2025
                                        logo
                                        • Anmelden

                                        • Du hast noch kein Konto? Registrieren

                                        • Anmelden oder registrieren, um zu suchen
                                        • Erster Beitrag
                                          Letzter Beitrag
                                        0
                                        • Home
                                        • Aktuell
                                        • Tags
                                        • Ungelesen 0
                                        • Kategorien
                                        • Unreplied
                                        • Beliebt
                                        • GitHub
                                        • Docu
                                        • Hilfe