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
    11
    1
    344

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    24
    1
    1.6k

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

Построение графиков 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.
  • 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

                            747

                            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