Grafana dashboard export in PDF

Grafana dashboard export in PDF

Thanks to @dumy for it's guides you can find it here:
https://flows.nodered.org/flow/482d3b2c9736fc1544dd62b50778d8d9
I have made some changes to export the page in "Dark mode" and i have added some "tutorials" for Puppeteer

Requisites:
-node-red-contrib-string - https://flows.nodered.org/node/node-red-contrib-string
-Puppeteer
This can be installed with

npm i puppeteer 

Than you have to go to your node-red installation folder and change settings.js and add this line

puppeteer:require('puppeteer')

under "functionGlobalContext"

Build Status

Setup:

In setting dashboard go to the module link and set link from Node-Red ❤️ N.B. Remeber to flag "Include current time range"

http://localhost:1880/grafana/${__dashboard.uid}/${__dashboard.name}

Change "localhost:1880" with your ip/dns

Build Status

Resulted:

Build Status

When you click the button this flow will be executed:

Build Status

In the node function there are 3 parameters that needs to be change:

// URL to load should be passed as first parameter const url = 'http://localhost:3000/d/' + msg.req.params.uid + '/' + msg.req.params.name +'?orgId=1&kiosk&from=' + msg.payload.from + '&to=' + msg.payload.to;

// Username and password (with colon separator) should be second parameter const auth_string = 'username:password!';

// Output file name should be third parameter const outfile = '';

Build Status

After call the link resulted in pdf

Build Status

it is a simple method to export grafana pages in pdf,

Thank you ✌️

[{"id":"39737d292e7049b0","type":"tab","label":"Flow 2","disabled":false,"info":"","env":[]},{"id":"1dd5c082c89e4ce8","type":"change","z":"39737d292e7049b0","name":"Set Headers","rules":[{"t":"set","p":"headers","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"headers.content-type","pt":"msg","to":"application/pdf","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":220,"wires":[["c33065c85ad230e5"]]},{"id":"c33065c85ad230e5","type":"http response","z":"39737d292e7049b0","name":"","x":1190,"y":220,"wires":[]},{"id":"0450c51d52c34862","type":"debug","z":"39737d292e7049b0","name":"debug 34","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":420,"y":180,"wires":[]},{"id":"99e0ba3034fd43bc","type":"string","z":"39737d292e7049b0","name":"replace","methods":[{"name":"replaceAll","params":[{"type":"str","value":" "},{"type":"str","value":"-"}]},{"name":"toLowerCase","params":[]}],"prop":"req.params.name","propout":"req.params.name","object":"msg","objectout":"msg","x":420,"y":220,"wires":[["6941f6754120136f"]]},{"id":"6941f6754120136f","type":"switch","z":"39737d292e7049b0","name":"","property":"payload.Light","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":590,"y":220,"wires":[["cf908096a8907dda","02673b9ff34538fe"]]},{"id":"02673b9ff34538fe","type":"function","z":"39737d292e7049b0","name":"exportToPDFDark","func":"// @ts-nocheck\n\n// const puppeteer = require('puppeteer');\nconst puppeteer = global.get(\"puppeteer\");\n// const odbc = global.get(\"odbc\");\n\n// URL to load should be passed as first parameter\nconst url = 'http://localhost:3000/d/' + msg.req.params.uid + '/' + msg.req.params.name +'?orgId=1&kiosk&from=' + msg.payload.from + '&to=' + msg.payload.to;\n// Username and password (with colon separator) should be second parameter\n\nconst auth_string = 'username:password';\n// Output file name should be third parameter\nconst outfile = '' + msg.req.params.uid+'.pdf';\n\n\n// TODO: Output an error message if number of arguments is not right or arguments are invalid\n\n// Set the browser width in pixels. The paper size will be calculated on the basus of 96dpi,\n// so 1200 corresponds to 12.5\".\nconst width_px = 1920;\n// Note that to get an actual paper size, e.g. Letter, you will want to *not* simply set the pixel\n// size here, since that would lead to a \"mobile-sized\" screen (816px), and mess up the rendering.\n// Instead, set e.g. double the size here (1632px), and call page.pdf() with format: 'Letter' and\n// scale = 0.5.\n\n// Generate authorization header for basic auth\n// @ts-ignore\nconst auth_header = 'Basic ' + new Buffer.from(auth_string).toString('base64');\n\n\n    const browser = await puppeteer.launch({headless:\"new\"});\n    const page = await browser.newPage();\n\n    // Set basic auth headers\n    await page.setExtraHTTPHeaders({ 'Authorization': auth_header });\n\n    // Increase timeout from the default of 30 seconds to 120 seconds, to allow for slow-loading panels\n    await page.setDefaultNavigationTimeout(1200000);\n\n    // Increasing the deviceScaleFactor gets a higher-resolution image. The width should be set to\n    // the same value as in page.pdf() below. The height is not important\n    await page.setViewport({\n        width: width_px,\n        height: 5000,\n        deviceScaleFactor: 2,\n        isMobile: false\n    })\n\n    // Wait until all network connections are closed (and none are opened withing 0.5s).\n    // In some cases it may be appropriate to change this to {waitUntil: 'networkidle2'},\n    // which stops when there are only 2 or fewer connections remaining.\n    await page.goto(url, { waitUntil: 'networkidle0' });\n\n    // Hide all panel description (top-left \"i\") pop-up handles and, all panel resize handles\n    // Annoyingly, it seems you can't concatenate the two object collections into one\n    await page.evaluate(() => {\n        let infoCorners = document.getElementsByClassName('panel-info-corner');\n        for (el of infoCorners) { el.hidden = true; };\n        let resizeHandles = document.getElementsByClassName('react-resizable-handle');\n        for (el of resizeHandles) { el.hidden = true; };\n    });\n    \n    // Get the height of the main canvas, and add a margin\n    var height_px = 1200;\n    height_px = await page.evaluate(() => {\n        return document.getElementsByClassName('react-grid-layout')[0].getBoundingClientRect().bottom;\n    }) + 20;\n    \n    await page.addStyleTag({\n      content: `\n        html {\n          -webkit-print-color-adjust: exact !important;\n          -webkit-filter: opacity(1) !important;\n        }\n      `\n    });\n    \n    const pdf = await page.pdf({\n        // path: outfile,\n        width: width_px + 'px',\n        height: height_px + 'px',\n        // format: 'A4',\n        // landscape: true,\n        //    format: 'Letter', <-- see note above for generating \"paper-sized\" outputs\n        scale: 1,\n        displayHeaderFooter: false,\n        margin: {\n            top: 0,\n            right: 0,\n            bottom: 0,\n            left: 0,\n        },\n    });\n   \n    await browser.close();\nmsg.topic = url;\nmsg.payload = pdf;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":790,"y":220,"wires":[["1dd5c082c89e4ce8"]]},{"id":"cf908096a8907dda","type":"debug","z":"39737d292e7049b0","name":"debug 36","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":760,"y":180,"wires":[]},{"id":"895671cc7f376f8c","type":"http in","z":"39737d292e7049b0","name":"","url":"/grafana/:uid/:name","method":"get","upload":false,"swaggerDoc":"","x":190,"y":220,"wires":[["99e0ba3034fd43bc","0450c51d52c34862"]]}]

Flow Info

Created 1 year, 10 months ago
Rating: 5 2

Owner

Actions

Rate:

Node Types

Core
  • change (x1)
  • debug (x2)
  • function (x1)
  • http in (x1)
  • http response (x1)
  • switch (x1)
Other

Tags

  • grafana
  • node-red
  • pdf
  • export
  • dashboard
  • puppeteer
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option