[{"data":1,"prerenderedAt":38835},["ShallowReactive",2],{"blog-\u002Fblog\u002Fnode-red\u002F2":3,"blog-all-for-related":4,"blog-all-node-red":5},null,[],[6,140,342,768,1473,2618,2787,3026,3126,3727,3832,3945,4231,4383,4515,4604,5634,6079,6221,7460,8413,8652,9253,9837,11052,11301,11802,12635,12956,13378,13771,14093,14561,15202,16539,18088,18161,18269,18376,18522,18923,19175,19413,19490,19740,21305,21872,22017,22242,22362,22405,22567,22644,22936,23064,23366,23764,24021,25447,26333,26909,28754,28936,29196,29295,29622,29855,30189,30356,30392,30495,30831,31039,31229,31375,36238,36729,36834,37115,37239,37338,37475,37573,37651,37840,38102,38146,38252,38566,38690,38751],{"id":7,"title":8,"authors":9,"body":11,"cta":110,"date":114,"description":115,"extension":116,"image":117,"lastUpdated":3,"meta":118,"navigation":129,"path":130,"seo":131,"sitemap":132,"stem":133,"subtitle":134,"tags":135,"tldr":3,"video":3,"__hash__":139},"blog\u002Fblog\u002F2026\u002F06\u002Fannouncing-node-red-con-2026.md","Announcing Node-RED Con 2026: Call for Papers Now Open",[10],"flowfuse",{"type":12,"value":13,"toc":102},"minimark",[14,23,30,35,38,60,73,77,86,90,93,99],[15,16,17,18,22],"p",{},"Node-RED Con is back. The community conference returns on ",[19,20,21],"strong",{},"Tuesday,\nNovember 3, 2026",", free, online, and open to everyone. FlowFuse is proud\nto sponsor this year's event alongside the OpenJS Foundation.",[15,24,25,26,29],{},"This year's theme is ",[19,27,28],{},"Node-RED at the Edge of Intelligence",".",[31,32,34],"h2",{"id":33},"the-call-for-papers-is-open","The Call for Papers is open",[15,36,37],{},"The conference agenda is built entirely from community submissions. The\norganizing team is looking for talks across three formats:",[39,40,41,48,54],"ul",{},[42,43,44,47],"li",{},[19,45,46],{},"Full Talks & Demos (25–30 mins):"," Technical talks, case studies, and\npractical demonstrations aligned with this year's theme. Submissions are\nparticularly encouraged in these areas: real-world Node-RED deployments in\nproduction, AI integration (LLMs, agents, MCP), edge computing architectures,\nindustrial use cases (PLCs, SCADA, OPC-UA, MQTT), and using AI tools to build\nand manage flows.",[42,49,50,53],{},[19,51,52],{},"Lightning Talks (8–10 mins):"," Fun, cool, or inspiring projects built with\nNode-RED, no matter how unconventional. IoT\u002FIIoT and AI projects are\nencouraged, but any project you're proud of is welcome.",[42,55,56,59],{},[19,57,58],{},"Panelists:"," Experts with strong opinions and experience in industrial\nautomation, AI in industrial environments, IoT security, or the future of\nlow-code development.",[15,61,62,69,70],{},[63,64,68],"a",{"href":65,"rel":66},"https:\u002F\u002Fnrcon.nodered.org\u002F",[67],"nofollow","Submit your talk"," ",[19,71,72],{},"CFP closes Friday, July 31, 2026.",[31,74,76],{"id":75},"why-speak","Why speak",[15,78,79,80,85],{},"Node-RED Con 2025 welcomed 1,700+ registered and 1,100+ attendees from 35+\ncountries. Sessions averaged 350+ live viewers, with the most-watched talks\ndrawing over 600 concurrent attendees. All sessions are recorded and published:\nthe ",[63,81,84],{"href":82,"rel":83},"https:\u002F\u002Fwww.youtube.com\u002Fplaylist?list=PLyNBB9VCLmo2yvFdVZOv41NUEzuw-CAZX",[67],"Node-RED Con 2025 playlist","\nhas accumulated 3,100+ views and is still growing.",[31,87,89],{"id":88},"stay-in-the-loop","Stay in the loop",[15,91,92],{},"Want to be notified when registration opens? Sign up below.",[94,95],"script",{"charSet":96,"type":97,"src":98},"utf-8","text\u002Fjavascript","\u002F\u002Fjs-eu1.hsforms.net\u002Fforms\u002Fembed\u002Fv2.js",[94,100,101],{},"\n  hbspt.forms.create({\n    portalId: \"26586079\",\n    formId: \"9a447b67-0957-4f40-90e9-69db1ab9c70a\",\n    region: \"eu1\"\n  });\n",{"title":103,"searchDepth":104,"depth":104,"links":105},"",4,[106,108,109],{"id":33,"depth":107,"text":34},2,{"id":75,"depth":107,"text":76},{"id":88,"depth":107,"text":89},{"type":111,"title":112,"description":113},"demo","See Node-RED running at scale","FlowFuse lets you deploy, manage, and govern Node-RED across your entire operation, from a single instance to hundreds of devices.","2026-06-29","Node-RED Con 2026 takes place on November 3, 2026. FlowFuse is proud to sponsor this free online conference. The Call for Papers is open until July 31, submit your talk.","md","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fnode-red-con-2026.png",{"excerpt":119},{"type":12,"value":120},[121,125],[15,122,17,123,22],{},[19,124,21],{},[15,126,25,127,29],{},[19,128,28],{},true,"\u002Fblog\u002F2026\u002F06\u002Fannouncing-node-red-con-2026",{"title":8,"description":115},{"loc":130},"blog\u002F2026\u002F06\u002Fannouncing-node-red-con-2026","The community conference returns on November 3, themed around Node-RED at the Edge of Intelligence.",[10,136,137,138],"news","community","node-red","fXtC23leREx8hm4KFwHJEx04f41qx8TTs4EZMpXDK_g",{"id":141,"title":142,"authors":143,"body":145,"cta":317,"date":321,"description":322,"extension":116,"image":323,"lastUpdated":3,"meta":324,"navigation":129,"path":333,"seo":334,"sitemap":335,"stem":336,"subtitle":337,"tags":338,"tldr":339,"video":340,"__hash__":341},"blog\u002Fblog\u002F2026\u002F06\u002Fnode-red-5-on-flowfuse.md","Node-RED 5 Is Here: What It Means for Industrial Teams",[144],"sumit-shinde",{"type":12,"value":146,"toc":311},[147,155,159,162,173,188,197,205,209,212,215,218,222,225,228,231,239,248,251,255,258,267,270,283,292,300,303],[15,148,149,150,29],{},"Node-RED 5.0 is out, the biggest change to the editor since the project began. Nick O'Leary, our CTO and Node-RED's co-creator, ",[63,151,154],{"href":152,"rel":153},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-5-now-available\u002F101239",[67],"announced it today on the Node-RED forum",[31,156,158],{"id":157},"whats-new","What's new",[15,160,161],{},"Node-RED 5 is the first release built around the editor itself. Previous major versions focused on the runtime and what Node-RED could connect to. This one reworks the place people spend their whole day.",[15,163,164,169],{},[165,166],"img",{"alt":167,"src":168},"The redesigned Node-RED 5 editor showing the new sidebars and Explorer panel.","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fnode-red-5.png",[170,171,172],"em",{},"The Node-RED 5 editor, redesigned around the place teams spend their whole day.",[15,174,175,176,179,180,183,184,187],{},"The ",[19,177,178],{},"redesigned sidebars"," now behave the same on both sides of the editor. Controls are grouped together instead of buried in a menu, and you can split one vertically to keep two panels open at once. A new ",[19,181,182],{},"Explorer panel"," gives you a structured view of your flows, making large projects with dozens of tabs far easier to navigate. And a ",[19,185,186],{},"built-in dark theme"," now ships in core, with no custom packages needed, alongside an accessibility review of the default theme.",[15,189,190,194],{},[165,191],{"alt":192,"src":193},"The Node-RED 5 editor in its built-in dark theme.","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fnr-dark-theme.png",[170,195,196],{},"The built-in dark theme now ships in core, with no custom package to install.",[15,198,199,200,29],{},"Underneath the visual changes are functional and security improvements across debugging, authentication, and TLS, the parts that matter most once Node-RED is running in production. If previous releases expanded what it could do, this one sharpens how it feels to use. For the full list, read the ",[63,201,204],{"href":202,"rel":203},"https:\u002F\u002Fnodered.org\u002Fblog\u002F2026\u002F06\u002F09\u002Fversion-5-0-released",[67],"release blog",[31,206,208],{"id":207},"what-it-means-for-industrial-teams","What it means for industrial teams",[15,210,211],{},"The editor is where your team spends its day, so changes here add up fast across a working week.",[15,213,214],{},"Most of what's new pulls in the same direction: less time fighting the tool, more time on the flow. The Explorer panel gives you a structured view of the whole project, so a flow set that spans dozens of tabs across several developers stops being a scavenger hunt. Picking up an unfamiliar flow, or onboarding someone new to it, takes seconds. The redesigned sidebars cut friction the other way: controls sit in the same place on both sides, and splitting one vertically keeps your debug output and node config open at once, so there are fewer clicks between the thing you're building and the thing telling you whether it works.",[15,216,217],{},"For teams running long sessions on a plant floor or in a control room, the built-in dark theme, now in core with no custom package to install and maintain, and the accessibility review of the default theme mean a readable, lower-strain interface out of the box.",[31,219,221],{"id":220},"flowfuse-management-and-ai-powered-flow-building-now-with-node-red-5","FlowFuse Management and AI-Powered Flow Building, Now with Node-RED 5",[15,223,224],{},"Node-RED is an editor and runtime. Operating it across teams, sites, and production environments is a different challenge entirely.",[15,226,227],{},"FlowFuse provides the platform capabilities industrial teams need to run Node-RED at scale: team collaboration, role-based access control, version control and deployment pipelines, SSO, audit logs, TLS-enabled instances, self-hosted and air-gapped deployments, and SOC 2 compliance.",[15,229,230],{},"The Node-RED 5 stack is available on FlowFuse Cloud today, allowing teams to take advantage of the redesigned editor, Explorer panel, built-in dark theme, and the latest platform improvements as soon as they upgrade their instances.",[15,232,233,234,238],{},"Alongside Node-RED 5, FlowFuse continues to improve how teams build applications. With the ",[63,235,237],{"href":236},"\u002Fblog\u002F2026\u002F06\u002Fflowfuse-release-2-31\u002F#expert-agentic-ga","FlowFuse 2.31 release",", FlowFuse Expert introduced agentic application building in open beta. Describe your application requirements in plain language, and FlowFuse Expert generates a working starting flow directly in your workspace for both Hosted and Remote Instances.",[15,240,241,245],{},[165,242],{"alt":243,"src":244},"FlowFuse Expert generating a starting flow in a Node-RED 5 workspace from a plain-language prompt.","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fflowfuse-expert-node-red-5.png",[170,246,247],{},"FlowFuse Expert builds a working starting flow from a plain-language prompt, now on Node-RED 5.",[15,249,250],{},"Teams can take advantage of Node-RED 5's redesigned editor while using FlowFuse to manage, secure, deploy, and scale their applications across production environments.",[31,252,254],{"id":253},"upgrading-your-instances","Upgrading your instances",[15,256,257],{},"Before you start, check one thing for edge hardware: Node-RED 5 needs Node.js 22.9 or later and drops 32-bit ARM, so hardware that can't run a 64-bit OS, such as the Raspberry Pi 3B and earlier, won't run it.",[259,260,261],"blockquote",{},[15,262,263,266],{},[19,264,265],{},"Note:"," Changing an instance's Node-RED version requires a restart. Your flows won't run while this happens.",[15,268,269],{},"To upgrade a FlowFuse Hosted Instance:",[271,272,273,276],"ol",{},[42,274,275],{},"Open the instance settings.",[42,277,278,279,282],{},"Click the ",[19,280,281],{},"Change Node-RED Version"," button, then select the Node-RED 5 stack from the dropdown.",[15,284,285,289],{},[165,286],{"alt":287,"src":288},"The FlowFuse instance settings showing the Node-RED 5 stack selected from the version dropdown.","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fnode-red-5-stack-settings.png",[170,290,291],{},"Select the Node-RED 5 stack in the instance settings, then apply the change.",[271,293,295],{"start":294},3,[42,296,297,298,29],{},"Click ",[19,299,281],{},[15,301,302],{},"That's it. Once the instance restarts, you'll be running Node-RED 5 and can start using the redesigned editor, Explorer panel, built-in dark theme, and the latest platform improvements immediately.",[15,304,305,306,310],{},"To avoid having to manually update your instances in the future, enable automatic stack updates. For production deployments, FlowFuse's ",[63,307,309],{"href":308},"\u002Fchangelog\u002F2025\u002F12\u002Fscheduled-maintenance\u002F","Scheduled Maintenance"," feature lets you control when updates and restarts are applied, ensuring changes are rolled out during planned maintenance windows.",{"title":103,"searchDepth":104,"depth":104,"links":312},[313,314,315,316],{"id":157,"depth":107,"text":158},{"id":207,"depth":107,"text":208},{"id":220,"depth":107,"text":221},{"id":253,"depth":107,"text":254},{"type":318,"title":319,"description":320},"contact","Get Node-RED 5 in production today","The Node-RED 5 stack is live on FlowFuse Cloud. Get the new editor plus the access controls, version control, SSO, secure deployments, and SOC 2 compliance an enterprise needs to run it at scale.","2026-06-09","Node-RED 5 reshapes the editor with redesigned sidebars, an Explorer panel, and a built-in dark theme. Here's what changes for teams running it in production on FlowFuse, and how to upgrade your instances.","\u002Fblog\u002F2026\u002F06\u002Fimages\u002Fnode-red-5-flowfuse.png",{"keywords":325,"excerpt":326},"node-red 5, node-red 5.0, node-red editor, node-red dark theme, node-red explorer panel, flowfuse node-red, node-red upgrade, node-red stack, industrial node-red, node-red production",{"type":12,"value":327},[328],[15,329,149,330,29],{},[63,331,154],{"href":152,"rel":332},[67],"\u002Fblog\u002F2026\u002F06\u002Fnode-red-5-on-flowfuse",{"title":142,"description":322},{"loc":333},"blog\u002F2026\u002F06\u002Fnode-red-5-on-flowfuse","The biggest change to the editor since the project began, and it's already live on FlowFuse Cloud.",[10,138],"Node-RED 5 is out, the biggest change to the editor since the project began, and it's primarily an editor release: redesigned sidebars, a new Explorer panel, a built-in dark theme, and functional and security improvements across debugging, authentication, and TLS. The Node-RED 5 stack is already live on FlowFuse Cloud. To upgrade a Hosted Instance, open its settings, change the stack to Node-RED 5, and click Change Node-RED Version, the instance restarts on 5.0. Check edge hardware first: Node-RED 5 needs Node.js 22.9 or later and drops 32-bit ARM, so Raspberry Pi 3B and earlier won't run it.","fI7r7Cj-slI","CrcRPmZ_ce6-On8K0Li5L8Rn6mkGXbOi3hcGjWXavFc",{"id":343,"title":344,"authors":345,"body":346,"cta":747,"date":751,"description":752,"extension":116,"image":753,"lastUpdated":754,"meta":755,"navigation":129,"path":760,"seo":761,"sitemap":762,"stem":763,"subtitle":764,"tags":765,"tldr":766,"video":3,"__hash__":767},"blog\u002Fblog\u002F2026\u002F01\u002Fnode-red-history-community-industrial-iot-flowfuse.md","The Node-RED Story: How Visual Programming Escaped the Lab and Conquered Industry",[144],{"type":12,"value":347,"toc":728},[348,351,355,358,361,364,373,376,385,388,391,395,398,403,410,414,429,433,440,447,451,458,461,466,470,473,479,487,490,498,502,511,519,522,525,529,538,541,544,548,551,554,557,560,563,567,570,573,576,579,583,591,600,603,606,610,613,616,624,627,630,645,653,668,671,674,678,681,684,687,690,693,697,700,703,706,709,712,716,719,722,725],[15,349,350],{},"In late 2011, Nick O'Leary was a \"plumber\" of the digital age. As part of IBM's Emerging Technology Group, he spent his days building experimental projects that pushed the boundaries of what connected systems could do. But there was a problem: for every new experiment, he found himself writing the same boilerplate code over and over, the tedious \"wiring\" work needed to make different systems talk to each other.",[31,352,354],{"id":353},"the-birth-of-node-red","The Birth of Node-RED",[15,356,357],{},"The frustration was mounting. Each new project at IBM's Emerging Technology lab meant rewriting integration code from scratch. Nick knew there had to be a better way.",[15,359,360],{},"On a \"wet January day in 2013,\" Nick decided to try something new. He wanted a way to visualize mapping messages across an MQTT infrastructure. He turned to Node.js, then a relatively new technology, as the foundation.",[15,362,363],{},"Over a single weekend, he built a rough prototype that allowed him to drag boxes and draw wires instead of writing endless integration code. When he showed it to his colleague Dave Conway-Jones on Monday morning, Dave’s response was immediate and world-changing: \"Go on then.\"",[15,365,366,370],{},[165,367],{"alt":368,"dataZoomable":103,"src":369},"Dave Conway-Jones and Nick O'Leary, co-creators of Node-RED","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fnick-and-dave.png",[170,371,372],{},"Dave and Nick",[15,374,375],{},"The name was a light-hearted play on words. \"Node\" reflected the Node.js runtime and the flow-based model, while \"RED\" was a cheeky nod to \"Code Red.\" While some later suggested it stood for \"Rapid Event Developer,\" Nick and Dave never felt the need to formalize it. It was simply a vibrant alternative to the \"Big Blue\" corporate standard.",[15,377,378,382],{},[165,379],{"alt":380,"dataZoomable":103,"src":381},"Early version of Node-RED interface showing visual flow-based programming with connected nodes","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fearly-node-red-screenshot.png",[170,383,384],{},"Early Node-RED Screenshot",[15,386,387],{},"Those two words meant everything. Dave didn't just approve, he joined in. Together, they started refining the tool, adding features, testing it on real IBM projects. What began as Nick's weekend hack became their shared mission. Within weeks, they had transformed a rough prototype into something that actually worked in production. The tool that was supposed to save Nick time on one project was now saving their entire team hours every week.",[15,389,390],{},"Node-RED offered something different: freedom. Drag boxes. Connect wires. Make anything talk to anything. No permission required.",[31,392,394],{"id":393},"the-public-spark","The Public Spark",[15,396,397],{},"The transition from a lab tool to a public project wasn't a corporate rollout; it was a grassroots explosion. By late 2013, Nick and Dave had seen enough: Node-RED was too useful to keep locked inside the walls of IBM.",[399,400,402],"h3",{"id":401},"the-first-commit-september-5-2013","The First Commit: September 5, 2013",[15,404,405,406,409],{},"The public story of Node-RED began with a push, not a press release. On ",[19,407,408],{},"September 5, 2013",", the first code was uploaded to GitHub. It was a \"soft launch\", a way to see if the world actually needed a visual tool for \"wiring\" the Internet of Things. Within weeks, the community responded with an intensity that caught even the creators by surprise.",[399,411,413],{"id":412},"the-workshop-moment-wuthering-bytes","The \"Workshop\" Moment: Wuthering Bytes",[15,415,416,417,420,421,424,425,428],{},"The first external validation happened just a week after the GitHub release at the ",[19,418,419],{},"Wuthering Bytes"," technology festival. After Nick gave a brief, unprepared lightning talk, he walked into a workshop the following day and was stunned to see ",[19,422,423],{},"Node-RED on every single screen."," A workshop facilitator had discovered the code on GitHub just days earlier and immediately scrapped his original plan. He realized that instead of teaching students how to troubleshoot line-by-line syntax, he could use Node-RED to let them actually ",[170,426,427],{},"build"," something. In thirty minutes, the class had achieved what usually took an entire day. It was the first time Nick saw people he had never met using his creation to solve real problems.",[399,430,432],{"id":431},"the-london-nodejs-user-group-lnug","The London Node.js User Group (LNUG)",[15,434,435,436,439],{},"While the hardware community was the first to adopt it, the ",[19,437,438],{},"London Node.js User Group (LNUG)"," was where the project faced its first true technical trial. Standing before a room of seasoned JavaScript developers, Nick had a 30-minute slot to prove that Node-RED wasn't just a visual toy, it was a powerful tool built on the very event-driven architecture they loved. The talk was a massive success, generating immediate buzz across social media and solidifying Node-RED’s reputation within the developer community.",[441,442],"lite-youtube",{"videoid":443,"params":444,"style":445,"title":446},"CF7BGDj2_G8","rel=0&start=1740","margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;","LNUG October 2013: Node-RED - Nick O'Leary",[399,448,450],{"id":449},"the-big-stage-thingmonk-2013","The Big Stage: ThingMonk 2013",[15,452,453,454,457],{},"While the September meetups provided the spark, ",[19,455,456],{},"ThingMonk on December 3, 2013",", was the \"Big Bang.\" This was the first major professional and industrial stage for Node-RED. It was here that the industry elite, the architects of the \"Internet of Things\", realized that Node-RED was the answer to the \"glorious mess\" of incompatible protocols.",[15,459,460],{},"In his presentation, \"Wiring the Internet of Things,\" Nick showed that visual programming wasn't just a shortcut for beginners; it was a professional-grade solution for the most complex integration problems on the planet.",[441,462],{"videoid":463,"params":464,"style":445,"title":465},"zUoCJb0jzuo","rel=0","ThingMonk 2013: Wiring The Internet of Things",[399,467,469],{"id":468},"_2014-proving-the-concept","2014: Proving the Concept",[15,471,472],{},"If 2013 was the spark, 2014 was the foundation. This was the year Node-RED moved from a lab experiment to a professional tool.",[15,474,475,478],{},[19,476,477],{},"IBM's launch of Bluemix"," (now IBM Cloud) marked a pivotal moment, Node-RED became a flagship starter app, validating that visual programming could power enterprise-grade cloud applications. The year also saw multiple major version releases that enhanced the platform's capabilities.",[15,480,481,485],{},[165,482],{"alt":483,"dataZoomable":103,"src":484},"Node-RED featured as a flagship starter app on IBM Bluemix in 2014","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fibm-bluemix.png",[170,486,483],{},[15,488,489],{},"During this period, Nick took the project on the road, speaking at conferences and meetups to actively build the community around Node-RED. A major milestone came at QCon London 2014, a moment Nick has often described as especially meaningful. The talk marked a turning point, demonstrating to the professional software industry that visual programming could be a serious, scalable solution for complex data integration.",[15,491,492,496],{},[165,493],{"alt":494,"dataZoomable":103,"src":495},"Nick O’Leary presenting Node-RED at QCon London 2014","\u002Fblog\u002F2026\u002F01\u002Fimages\u002FQ-CON-2014.png",[170,497,494],{},[31,499,501],{"id":500},"the-explosion-the-raspberry-pi-milestone","The Explosion: The Raspberry Pi Milestone",[15,503,504,505,510],{},"By November 2015, Node-RED was officially integrated into the Raspbian Jessie image, marking its transition from a manual install to a core component of the Raspberry Pi ecosystem. The ",[63,506,509],{"href":507,"rel":508},"https:\u002F\u002Fwww.raspberrypi.com\u002Fnews\u002Flatest-raspbian-update\u002F",[67],"announcement was made by Simon Long",", the Raspberry Pi Foundation's UX Engineer, who detailed the update in a blog post on December 2, 2015. This milestone meant that millions of $35 computers suddenly shipped with a visual programming tool built specifically for the \"Internet of Things,\" sitting prominently in the application menu alongside educational staples like Scratch and Python.",[15,512,513,517],{},[165,514],{"alt":515,"dataZoomable":103,"src":516},"Screenshot of the Raspbian Jessie desktop menu showing Node-RED included by default with Scratch and Python on Raspberry Pi","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fnode-red-on-pi.jpg",[170,518,515],{},[15,520,521],{},"The impact was immediate and massive. Students learning to code on Raspberry Pi discovered Node-RED next to Python. Hobbyists building weekend projects found it pre-installed. Educators teaching IoT concepts had a visual tool ready to use. What had required hunting through forums and installation guides was now just a click away in the menu. The barrier to entry dropped to zero, and the community exploded.",[15,523,524],{},"Within months, Node-RED flows for Raspberry Pi projects flooded online forums. Home automation guides assumed you had it installed. The \"install Node-RED\" step disappeared from tutorials, it was just there. This wasn't just about convenience. It was about legitimacy. Being bundled with Scratch and Python signaled that Node-RED wasn't a niche hack, it was essential infrastructure for the IoT era.",[31,526,528],{"id":527},"a-home-for-the-future-the-openjs-foundation","A Home for the Future: The OpenJS Foundation",[15,530,531,532,537],{},"By 2016, Node-RED had outgrown its \"side project\" status at IBM. To ensure the tool remained open and vendor-neutral, ",[63,533,536],{"href":534,"rel":535},"https:\u002F\u002Fnodered.org\u002Fblog\u002F2016\u002F10\u002F17\u002Fjs-foundation",[67],"IBM moved Node-RED into the newly formed JS Foundation"," (which merged in 2019 to become the OpenJS Foundation) as a founding project.",[15,539,540],{},"This was a pivotal moment for industrial adoption. By moving the project to a neutral foundation, Node-RED was no longer just an \"IBM tool\", it became a public utility. Large-scale industrial players like Hitachi and Siemens could now contribute to the code and build their own products on top of it without fear of \"vendor lock-in.\" It signaled that Node-RED was a stable, world-class piece of software infrastructure, governed by the same community standards as jQuery and Node.js.",[15,542,543],{},"The foundation move unlocked something crucial: trust at scale. When a Fortune 500 manufacturer evaluates tools, vendor neutrality isn't optional, it's mandatory. The OpenJS Foundation gave Node-RED the governance structure, intellectual property protections, and community oversight that enterprises require. Companies that would never bet their production lines on an IBM internal tool could now build on Node-RED with confidence. The path from hobbyist workshops to factory floors was open.",[31,545,547],{"id":546},"the-community-takes-over","The Community Takes Over",[15,549,550],{},"The community didn't just use Node-RED, they built it. By 2023, the library contained over 4,300 nodes. Every new smart device gets a Node-RED node within weeks. Every protocol got wrapped. Forums filled with shared flows. Someone in Germany would solve a lighting problem at 2 AM, share the solution, and someone in California would adapt it for their garage door an hour later.",[15,552,553],{},"This wasn't just a user base, it was a movement. Makers, hobbyists, and tinkerers around the world were solving problems and sharing solutions freely. The barrier to contribution was low, anyone could create a node and publish it. The barrier to adoption was even lower, anyone could install a node with a single click.",[15,555,556],{},"The community's creativity extended across domains. Nodes were built for popular platforms, industrial protocols, cloud services, databases, messaging systems, AI services, and thousands of integrations beyond. If a technology existed, someone had already made it work in Node-RED.",[15,558,559],{},"But something unexpected was happening beneath the surface. While the community shared home automation flows in forums, engineers in factories were quietly watching. They saw the same drag-and-drop simplicity that connected smart lights could connect production equipment. They saw the protocol-agnostic approach that worked for hobbyists could work for industrial systems. They saw a tool that both IT and operations teams could actually understand.",[15,561,562],{},"The movement that began in living rooms was about to reach factory floors.",[31,564,566],{"id":565},"the-bridge-nobody-planned","The Bridge Nobody Planned",[15,568,569],{},"The movement reached Hitachi's factory floors in a way nobody anticipated. Node-RED emerged as a crucial communication tool bridging the gap between IT and factory engineers. Its intuitive interface allowed the entire flow to be visible on screen, facilitating discussions and collaborative efforts seamlessly.",[15,571,572],{},"For decades, IT and OT spoke different languages. IT managed databases and networks. OT ran machines and production lines. They needed each other but couldn't understand each other. Node-RED gave them a shared vocabulary, visual flows that both sides could read.",[15,574,575],{},"A factory engineer could build a flow reading data from a Modbus PLC. An IT engineer could look at that same flow and immediately understand what it did. No translation needed. The same visual language that connected smart home devices was now connecting industrial equipment to enterprise systems.",[15,577,578],{},"The movement had arrived in the industry. And it brought its community-driven, protocol-agnostic approach with it.",[31,580,582],{"id":581},"the-numbers-prove-it","The Numbers Prove It",[15,584,585,590],{},[63,586,589],{"href":587,"rel":588},"https:\u002F\u002Fnodered.org\u002Fabout\u002Fcommunity\u002Fsurvey\u002F2019\u002F",[67],"The 2019"," survey captured the movement mid-expansion: 31.5% used it in manufacturing, and 24% had created PLC applications. Home automation remained strong, but the industry was already significant.",[15,592,593,594,599],{},"By ",[63,595,598],{"href":596,"rel":597},"https:\u002F\u002Fnodered.org\u002Fabout\u002Fcommunity\u002Fsurvey\u002F2023\u002F",[67],"2023",", the industrial footprint had solidified. Manufacturing remained the second largest industry at 40%, up from 31.5%. Just over 40% now use Node-RED professionally. Home automation usage held steady at 70%, both sectors thriving, not competing.",[15,601,602],{},"The protocols revealed the depth of industrial adoption. MQTT and HTTP dominated both sectors. But Modbus and OPC-UA, industrial protocols, saw significant growth. These weren't hobbyists experimenting. These were engineers connecting real production equipment.",[15,604,605],{},"The movement had conquered two worlds simultaneously.",[31,607,609],{"id":608},"flowfuse-conquering-industrial-iot","FlowFuse: Conquering Industrial IoT",[15,611,612],{},"By 2021, the challenge had shifted. Node-RED worked brilliantly for individual developers, but enterprises needed more: how do you scale it across tens of thousands of factory instances? How do you secure it? How do you make teams collaborate on flows without stepping on each other's work? To solve these problems, Nick founded FlowFuse (formerly FlowForge).",[15,614,615],{},"But a technical revolution needs both an inventor and an architect. In January 2022, Zeger-Jan van de Weg (ZJ) joined as CEO. Drawing on his experience as an early GitLab employee, ZJ knew how to turn a successful open-source project into an enterprise-grade platform. While Nick continues to guide the technical direction as CTO, ZJ leads the effort to scale industrial automation for Fortune 500 companies.",[15,617,618,622],{},[165,619],{"alt":620,"dataZoomable":103,"src":621},"Nick and ZJ","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fzj-and-nick.png",[170,623,620],{},[15,625,626],{},"Together, they have raised $14.5 million to build the infrastructure enterprises demand. With this funding, they assembled a talented team spanning engineering, product, customer success, marketing, sales, and community, each member contributing their unique expertise to drive FlowFuse's growth across every aspect of the business.",[15,628,629],{},"Between 2021 and 2025, FlowFuse evolved into a full industrial ecosystem through deliberate focus on operational maturity. The journey began with team collaboration and scaling, introducing version control, team permissions, and DevOps pipelines that let engineering teams work together on flows like software projects, then deploy updates across thousands of remote Node-RED instances with discipline. With collaboration and scale established, the focus shifted to enterprise security, adding SSO, RBAC, and SOC 2 compliance to meet strict governance requirements. Next came infrastructure independence, integrating a built-in MQTT broker and FlowFuse Tables to reduce reliance on external services. By 2025, as AI began reshaping industry, FlowFuse entered the intelligence era with MCP nodes, and FlowFuse Expert, an AI that converts plain-English industrial problems directly into working Node-RED flows.",[15,631,632,633,638,639,644],{},"FlowFuse's mission went beyond enterprise features. It invested continuously in Node-RED core development and community sustainability. When the original ",[63,634,637],{"href":635,"rel":636},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fannouncement-node-red-dashboard-v1-deprecation-notice\u002F89006\u002F2",[67],"Node-RED Dashboard was deprecated",", FlowFuse rebuilt ",[63,640,643],{"href":641,"rel":642},"https:\u002F\u002Fdashboard.flowfuse.com",[67],"Node-RED Dashboard 2.0"," from the ground up.",[15,646,647,648,652],{},"That commitment extended to the ecosystem itself. Through the ",[63,649,651],{"href":650},"\u002Fblog\u002F2025\u002F07\u002Fcertified-nodes-v2\u002F","Certified Nodes program",", FlowFuse identifies critical community nodes and subjects them to rigorous security and quality testing. To ensure long-term reliability, FlowFuse supports maintainers financially or assumes maintenance responsibility, an approach aligned with the expectations of large industrial organizations.",[15,654,655,656,661,662,667],{},"Education and community came next. FlowFuse launched ",[63,657,660],{"href":658,"rel":659},"https:\u002F\u002Fnode-red-academy.learnworlds.com",[67],"Node-RED Academy",", the first official Node-RED Academy offering Node-RED certification, providing a clear path from learning flows to building production-ready systems. This was followed by major investment in ",[63,663,666],{"href":664,"rel":665},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-survey-shaping-the-future-of-node-reds-user-experience\u002F98346\u002F95",[67],"modernizing the Node-RED editor UI"," through an open, community-driven process. The journey culminated in the Node-RED Conference, bringing together over 1,000 live attendees worldwide and transforming a distributed user base into a connected industrial community.",[15,669,670],{},"Five years into its journey, FlowFuse stands as a rare example of open source scaling into industrial reality without losing its soul. What started as a way to operate Node-RED at scale is now a production platform deployed in some of the world's largest factories, increasingly taking on responsibilities once dominated by legacy industrial platforms. It demonstrates that when community stewardship, engineering rigor, and long-term vision align, open source can form the backbone of modern industrial systems.",[15,672,673],{},"This vision has translated into explosive market adoption, with the company multiplying its annual recurring revenue and customer base by nearly 5x in the last year alone.",[31,675,677],{"id":676},"why-it-succeeded","Why It Succeeded",[15,679,680],{},"Node-RED won because it was simple when everyone else was complicated.",[15,682,683],{},"The smart home industry sold subscriptions and proprietary hubs. Industrial automation sold certifications and consulting contracts. Both pretended connecting systems required expertise only they could provide.",[15,685,686],{},"Node-RED proved them wrong. Drag a box. Draw a line. Done.",[15,688,689],{},"The same principles work everywhere. A homeowner connecting a thermostat to weather data uses the exact concepts an engineer uses connecting factory equipment to a data lake. Input. Logic. Output. The scale changes. The principles don't.",[15,691,692],{},"The visual interface removed the gatekeepers. You didn't need to understand APIs or write integration code. You could see the logic, understand it, and modify it, all on one screen. When both the factory floor worker and the IT manager can look at the same flow and grasp what it does, you've eliminated the translation layer that created vendor dependency.",[31,694,696],{"id":695},"today-and-tomorrow","Today and Tomorrow",[15,698,699],{},"Today, Node-RED spans from living rooms to factory floors. FlowFuse has made it enterprise-ready.",[15,701,702],{},"Home users still rely on Node-RED to make incompatible smart devices work together. But FlowFuse has transformed industrial adoption, turning a powerful tool into production infrastructure with fleet management, enterprise security, and DevOps pipelines that Fortune 500 companies trust.",[15,704,705],{},"The scale is substantial. Manufacturing plants run FlowFuse across thousands of remote sites. Energy companies depend on it for critical infrastructure monitoring. What started as visual programming for hobbyists now handles mission-critical operations where downtime costs millions per hour.",[15,707,708],{},"The momentum continues building. FlowFuse is pushing Node-RED into new territory, AI-powered flow generation, advanced analytics, and capabilities that didn't exist two years ago. As AI reshapes manufacturing and operations, Node-RED is positioned at the center of that transformation.",[15,710,711],{},"The revolution that began in living rooms and reached factory floors is entering its next phase.",[31,713,715],{"id":714},"final-note","Final Note",[15,717,718],{},"Writing this reminded me why I fell in love with Node-RED. But the real story isn't in these words, it's unfolding right now in someone's garage, on a factory floor, in a student's first \"it works!\" moment.",[15,720,721],{},"What amazes me most is the community's generosity. People don't just solve problems, they share them, celebrate each other's wins. Curiosity matters more than credentials. \"I don't know, but let's figure it out together\" is the standard response.",[15,723,724],{},"If this story touched you, please share it. Someone out there might need the reminder that the impossible is usually just the not-yet-possible.",[15,726,727],{},"Thank you for reading, and happy New Year!",{"title":103,"searchDepth":104,"depth":104,"links":729},[730,731,738,739,740,741,742,743,744,745,746],{"id":353,"depth":107,"text":354},{"id":393,"depth":107,"text":394,"children":732},[733,734,735,736,737],{"id":401,"depth":294,"text":402},{"id":412,"depth":294,"text":413},{"id":431,"depth":294,"text":432},{"id":449,"depth":294,"text":450},{"id":468,"depth":294,"text":469},{"id":500,"depth":107,"text":501},{"id":527,"depth":107,"text":528},{"id":546,"depth":107,"text":547},{"id":565,"depth":107,"text":566},{"id":581,"depth":107,"text":582},{"id":608,"depth":107,"text":609},{"id":676,"depth":107,"text":677},{"id":695,"depth":107,"text":696},{"id":714,"depth":107,"text":715},{"type":748,"title":749,"description":750},"sign-up","Ready to Take Node-RED to Production?","FlowFuse gives Node-RED everything enterprises need, fleet management, version control, security, and DevOps pipelines. See how teams are scaling from one instance to thousands.","2026-01-05","Read how Node-RED evolved from Nick O'Leary's weekend IBM project into a global phenomenon, powering millions of home assistance setups, sparking a Raspberry Pi revolution, building a 4,300+ node community, and conquering industrial IoT with FlowFuse's enterprise platform.","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fnode-red-story.png","2026-06-17",{"keywords":3,"excerpt":756},{"type":12,"value":757},[758],[15,759,350],{},"\u002Fblog\u002F2026\u002F01\u002Fnode-red-history-community-industrial-iot-flowfuse",{"title":344,"description":752},{"loc":760},"blog\u002F2026\u002F01\u002Fnode-red-history-community-industrial-iot-flowfuse","From a single weekend hack to millions of deployments across homes and industry.",[138],"Node-RED began as Nick O'Leary's weekend prototype at IBM in January 2013, went public on GitHub that September, and exploded in popularity after being bundled with the Raspberry Pi in 2015. Moving to the OpenJS Foundation in 2016 gave it the vendor-neutrality enterprises needed, and a community-built library of 4,300+ nodes pushed it from homes into industry. In 2021 Nick founded FlowFuse to scale Node-RED for enterprises, raising $14.5M and adding fleet management, security, DevOps pipelines, and AI-powered flow generation for Fortune 500 manufacturers.","n5bOctpy-R7_HA5mn1Sx5u7jT2twLGSfVbVHWWMLThM",{"id":769,"title":770,"authors":771,"body":772,"cta":3,"date":1455,"description":1456,"extension":116,"image":1457,"lastUpdated":1458,"meta":1459,"navigation":129,"path":1465,"seo":1466,"sitemap":1467,"stem":1468,"subtitle":1469,"tags":1470,"tldr":1471,"video":3,"__hash__":1472},"blog\u002Fblog\u002F2025\u002F12\u002Fnode-red-timer.md","Node-RED Timer Tutorial: Create Stopwatch and Countdown Timers",[144],{"type":12,"value":773,"toc":1443},[774,777,784,793,805,817,821,824,844,873,876,881,892,895,899,906,931,936,940,947,979,982,986,993,1061,1072,1075,1083,1089,1100,1103,1151,1155,1158,1164,1169,1175,1195,1198,1202,1209,1332,1343,1351,1354,1357,1386,1406,1422,1426,1434,1440],[15,775,776],{},"Timers are everywhere in industrial automation. You need them to track how long a machine has been running, measure downtime, schedule maintenance, or coordinate processes that happen in sequence.",[15,778,779,780,783],{},"Through my work as a technical writer covering IIoT, building industrial applications, and being part of a company where our customers deploy real-world automation systems, I've observed that ",[19,781,782],{},"timers"," are ubiquitous in production environments. Whether it's a production floor display showing machine runtime, a maintenance terminal counting down to the next service window, or an operator interface tracking downtime by reason code, timers are foundational to industrial visibility. Here's an example of a performance operator terminal with timer functionality:",[15,785,786,790],{},[165,787],{"alt":788,"dataZoomable":103,"src":789},"Performance Operator Terminal","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fperformance-operator-terminal.png",[170,791,792],{},"Performance operator terminal displaying real-time production metrics with timer functionality",[15,794,795,796,800,801,804],{},"If you want to explore this interface yourself, you can ",[63,797,799],{"href":798},"\u002Fblueprints\u002Fmanufacturing\u002Fperformance-operator-terminal\u002F","deploy this blueprint"," to your ",[19,802,803],{},"FlowFuse"," instance and start experimenting immediately.",[15,806,807,808,812,813,816],{},"In this article, we'll build two types of timers in ",[63,809,811],{"href":810},"\u002F","Node-RED",": ",[19,814,815],{},"stopwatches"," that measure elapsed time and countdowns that trigger actions after a set duration. Both are straightforward to implement and essential for most automation projects.",[31,818,820],{"id":819},"prerequisites","Prerequisites",[15,822,823],{},"Before following this tutorial, you should have:",[39,825,826],{},[42,827,828,829,832,833,837,838,843],{},"A running ",[19,830,831],{},"Node-RED instance",". For professional development and production deployments, ",[19,834,835],{},[63,836,803],{"href":810}," is the recommended platform. If you don’t have an account, ",[63,839,842],{"href":840,"rel":841},"http:\u002F\u002Fapp.flowfuse.com",[67],"start your free trial"," today.",[259,845,846],{},[15,847,848,849,853,854,853,858,853,862,866,867,872],{},"FlowFuse provides enterprise-grade Node-RED hosting with managed infrastructure, built-in ",[63,850,852],{"href":851},"\u002Fblog\u002F2024\u002F10\u002Fhow-to-build-automate-devops-pipelines-node-red-deployments\u002F","DevOps tools",", ",[63,855,857],{"href":856},"\u002Fblog\u002F2025\u002F10\u002Fplc-to-mqtt-using-flowfuse\u002F#step-3%3A-set-up-mqtt-with-flowfuse","MQTT broker",[63,859,861],{"href":860},"\u002Fblog\u002F2025\u002F08\u002Fgetting-started-with-flowfuse-tables\u002F","database",[63,863,865],{"href":864},"\u002Fblog\u002F2024\u002F12\u002Fflowfuse-team-collaboration\u002F","team collaboration"," with ",[63,868,871],{"href":869,"rel":870},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=mb1s1YQIpZY",[67],"granular access control",", and seamless scaling capabilities. Whether you need cloud-hosted instances managed by FlowFuse or remote instances running on your edge devices, the platform provides unified management for both. FlowFuse also includes AI-powered features that accelerate flow development and streamline automation projects. From prototyping to mission-critical production deployments, FlowFuse handles the infrastructure complexity so you can focus on building flows.",[15,874,875],{},"That's all you need to get started.",[877,878,880],"h1",{"id":879},"getting-started","Getting Started",[15,882,883,884,887,888,891],{},"We'll use two Node-RED community nodes for this tutorial: ",[19,885,886],{},"node-red-contrib-hourglass"," for stopwatch timers and node-red-contrib-countdown for ",[19,889,890],{},"countdown timers",". Both are simple to configure and work reliably in production environments.",[15,893,894],{},"Let's start with installing and building a stopwatch.",[399,896,898],{"id":897},"installing-the-hourglass-node","Installing the Hourglass Node",[15,900,901,902,905],{},"First, install the ",[19,903,904],{},"hourglass node"," in your Node-RED instance:",[271,907,908,911,917,920,926],{},[42,909,910],{},"Open the Node-RED editor",[42,912,278,913,916],{},[19,914,915],{},"hamburger menu"," (top right) and select \"Manage palette\"",[42,918,919],{},"Go to the \"Install\" tab",[42,921,922,923],{},"Search for ",[924,925,886],"code",{},[42,927,297,928],{},[19,929,930],{},"\"Install\"",[15,932,175,933,935],{},[19,934,904],{}," will appear in your palette under the function category.",[31,937,939],{"id":938},"understanding-hourglass-commands","Understanding Hourglass Commands",[15,941,942,943,946],{},"The hourglass node responds to control commands sent via ",[924,944,945],{},"msg.command",". The available commands are:",[39,948,949,955,961,967,973],{},[42,950,951,954],{},[19,952,953],{},"start"," - Begins timing operation",[42,956,957,960],{},[19,958,959],{},"stop"," - Stops the timer and outputs elapsed time",[42,962,963,966],{},[19,964,965],{},"pause"," - Pauses timing without resetting",[42,968,969,972],{},[19,970,971],{},"reset"," - Clears timer state and stops operation",[42,974,975,978],{},[19,976,977],{},"status"," - Outputs current timer state without stopping",[15,980,981],{},"These commands control timer behavior.",[399,983,985],{"id":984},"implementing-a-stopwatch-timer","Implementing a Stopwatch Timer",[15,987,988,989,992],{},"Build a basic ",[19,990,991],{},"stopwatch flow",":",[271,994,995,998,1005,1043,1049,1055,1058],{},[42,996,997],{},"Drag four inject nodes onto the canvas",[42,999,1000,1001,1004],{},"Add four ",[19,1002,1003],{},"change nodes",", one after each inject node, and connect them",[42,1006,1007,1008,1010,1011],{},"Configure the change nodes to set ",[924,1009,945],{},":\n",[39,1012,1013,1023,1029,1037],{},[42,1014,1015,1016,1019,1020],{},"First ",[19,1017,1018],{},"change node",": set to ",[924,1021,1022],{},"\"start\"",[42,1024,1025,1026],{},"Second change node: set to ",[924,1027,1028],{},"\"status\"",[42,1030,1031,1032,1019,1034],{},"Third ",[19,1033,1018],{},[924,1035,1036],{},"\"stop\"",[42,1038,1039,1040],{},"Fourth change node: set to ",[924,1041,1042],{},"\"reset\"",[42,1044,1045,1046,1048],{},"Drag an ",[19,1047,904],{}," onto the canvas",[42,1050,1051,1052,1054],{},"Connect all four change nodes to the ",[19,1053,904],{}," input",[42,1056,1057],{},"Add a debug node and connect it to the hourglass node output",[42,1059,1060],{},"Deploy the flow",[15,1062,1063,1064,1067,1068,1071],{},"When you trigger the first inject node, the ",[19,1065,1066],{},"stopwatch starts",". The second inject node checks current elapsed time without stopping the timer, you can configure this inject node to repeat at intervals for continuous time monitoring. The third inject node stops the ",[19,1069,1070],{},"stopwatch"," and outputs the total elapsed time. The fourth inject node resets the stopwatch back to zero.",[15,1073,1074],{},"The node outputs a message containing timing information:",[15,1076,1077,1081],{},[165,1078],{"alt":1079,"dataZoomable":103,"src":1080},"Hourglass node output message showing elapsed time in multiple formats","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fhourglass-output.png",[170,1082,1079],{},[15,1084,175,1085,1088],{},[924,1086,1087],{},"elapsed"," object provides multiple formats: a human-readable string, total milliseconds, and broken-down time components. This flexibility supports different display requirements and calculation needs.",[15,1090,1091,1092,1095,1096,1099],{},"This basic example demonstrates the pattern. In production applications, replace the inject nodes with actual process triggers, ",[19,1093,1094],{},"machine status signals"," from PLCs or MQTT messages from sensors. The ",[19,1097,1098],{},"hourglass node output"," then connects to databases for logging, dashboards for visualization, or notification systems for alerts when thresholds are exceeded.",[15,1101,1102],{},"Below is the complete flow.",[15,1104,1105,1106,1150],{},"{% renderFlow 300 %}\n",[1107,1108,1109,1110,1113,1114,1117,1118,1121,1122,1125,1126,1129,1130,1133,1134,1136,1137,1139,1140,1143,1144,1146,1147,1149],"span",{},"{\"id\":\"a8e05e4b1a8a8e5e\",\"type\":\"group\",\"z\":\"b446dfa04d79d359\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,1111,1112],{},"\"81365fac1ccf6299\",\"65cf5a523eaee926\",\"560a9271f2b46687\",\"4c5449af7a4dd6ef\",\"e8400461cd50b356\",\"55a008aab6204565\",\"18630deedc38af9f\",\"9ef4a745bd0194cf\",\"cf84add540c079ce\",\"35ab3bbb1674b4f6\"",",\"x\":194,\"y\":2231,\"w\":892,\"h\":230},{\"id\":\"81365fac1ccf6299\",\"type\":\"hourglass\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"\",\"humanizeLocale\":\"\",\"x\":820,\"y\":2340,\"wires\":[[\"9ef4a745bd0194cf\"]]},{\"id\":\"65cf5a523eaee926\",\"type\":\"inject\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Auto Start\",\"props\":",[1107,1115,1116],{},"{\"p\":\"payload\",\"v\":\"\",\"vt\":\"date\"},{\"p\":\"topic\",\"v\":\"\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":310,\"y\":2272,\"wires\":[[\"560a9271f2b46687\"]]},{\"id\":\"560a9271f2b46687\",\"type\":\"change\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Start\",\"rules\":",[1107,1119,1120],{},"{\"t\":\"set\",\"p\":\"command\",\"pt\":\"msg\",\"to\":\"start\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":570,\"y\":2272,\"wires\":[[\"81365fac1ccf6299\"]]},{\"id\":\"4c5449af7a4dd6ef\",\"type\":\"change\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Status\",\"rules\":",[1107,1123,1124],{},"{\"t\":\"set\",\"p\":\"command\",\"pt\":\"msg\",\"to\":\"status\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":570,\"y\":2320,\"wires\":[[\"81365fac1ccf6299\"]]},{\"id\":\"e8400461cd50b356\",\"type\":\"change\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Stop\",\"rules\":",[1107,1127,1128],{},"{\"t\":\"set\",\"p\":\"command\",\"pt\":\"msg\",\"to\":\"stop\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":570,\"y\":2368,\"wires\":[[\"81365fac1ccf6299\"]]},{\"id\":\"55a008aab6204565\",\"type\":\"inject\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Read every 1 seconds\",\"props\":",[1107,1131,1132],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}",",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":350,\"y\":2320,\"wires\":[[\"4c5449af7a4dd6ef\"]]},{\"id\":\"18630deedc38af9f\",\"type\":\"inject\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Manual Stop\",\"props\":",[1107,1135,1116],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":310,\"y\":2369,\"wires\":[[\"e8400461cd50b356\"]]},{\"id\":\"9ef4a745bd0194cf\",\"type\":\"debug\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Result\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":990,\"y\":2340,\"wires\":",[1107,1138],{},"},{\"id\":\"cf84add540c079ce\",\"type\":\"change\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Reset\",\"rules\":",[1107,1141,1142],{},"{\"t\":\"set\",\"p\":\"command\",\"pt\":\"msg\",\"to\":\"reset\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":570,\"y\":2420,\"wires\":[[\"81365fac1ccf6299\"]]},{\"id\":\"35ab3bbb1674b4f6\",\"type\":\"inject\",\"z\":\"b446dfa04d79d359\",\"g\":\"a8e05e4b1a8a8e5e\",\"name\":\"Manual Reset\",\"props\":",[1107,1145,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":310,\"y\":2420,\"wires\":[[\"cf84add540c079ce\"]]},{\"id\":\"5191dd25898c631c\",\"type\":\"global-config\",\"env\":",[1107,1148],{},",\"modules\":{\"node-red-contrib-hourglass\":\"1.5.0\"}}","\n{% endrenderFlow %}",[31,1152,1154],{"id":1153},"implementing-a-countdown-timer","Implementing a Countdown Timer",[15,1156,1157],{},"Countdown timers trigger actions after a specified duration. Common applications include scheduled maintenance windows, batch process timeouts, and timed equipment shutdowns.",[15,1159,1160,1161,29],{},"To implement a countdown, we will use ",[924,1162,1163],{},"node-red-contrib-countdown",[1165,1166,1168],"h4",{"id":1167},"installing-the-countdown-node","Installing the Countdown Node",[15,1170,1171,1172,905],{},"Install the ",[19,1173,1174],{},"countdown node",[271,1176,1177,1179,1185,1187,1191],{},[42,1178,910],{},[42,1180,1181,1182],{},"Click the hamburger menu (top right) and select ",[19,1183,1184],{},"\"Manage palette\"",[42,1186,919],{},[42,1188,922,1189],{},[924,1190,1163],{},[42,1192,297,1193],{},[19,1194,930],{},[15,1196,1197],{},"The countdown node will appear in your palette.",[399,1199,1201],{"id":1200},"building-a-countdown-timer","Building a Countdown Timer",[15,1203,1204,1205,1208],{},"Create a ",[19,1206,1207],{},"countdown timer flow"," in Node-RED:",[271,1210,1211,1217,1261,1313,1319,1326],{},[42,1212,1045,1213,1216],{},[19,1214,1215],{},"Inject"," node onto the canvas.",[42,1218,1219,1220],{},"Add a Change node and configure it to:",[39,1221,1222,1237,1246],{},[42,1223,1224,1225,1228,1229,1232,1233,1236],{},"Set ",[924,1226,1227],{},"msg.payload"," to the countdown duration in ",[19,1230,1231],{},"seconds"," (for example, ",[924,1234,1235],{},"50"," for 50 seconds).",[42,1238,1224,1239,1242,1243,29],{},[924,1240,1241],{},"msg.topic"," to ",[924,1244,1245],{},"\"control\"",[42,1247,1248,1249,1252,1253,1255,1256,1258,1259,29],{},"You can configure the countdown duration either in the ",[19,1250,1251],{},"Change"," node or directly in the Inject node, as long as ",[924,1254,1227],{}," is in milliseconds and ",[924,1257,1241],{}," is ",[924,1260,1245],{},[42,1262,1263,1264,1267,1268],{},"Drag a ",[19,1265,1266],{},"Countdown"," node onto the canvas and configure it with the following settings:",[39,1269,1270,1280,1289,1295,1301,1307],{},[42,1271,1272,1275,1276,1279],{},[19,1273,1274],{},"Timer On payload",": The message payload sent when the countdown starts (for example, ",[924,1277,1278],{},"true",").",[42,1281,1282,1285,1286,1279],{},[19,1283,1284],{},"Timer Off payload",": The message payload sent when the countdown completes (for example, ",[924,1287,1288],{},"false",[42,1290,1291,1294],{},[19,1292,1293],{},"Restart countdown if message is received while running",": Enable this to restart the countdown if a new control message is received.",[42,1296,1297,1300],{},[19,1298,1299],{},"Send output message on Reset",": Enable this to send a message when the countdown is reset.",[42,1302,1303,1306],{},[19,1304,1305],{},"Set time to new duration if control message is received while running",": Enable this to update the countdown duration while it is running.",[42,1308,1309,1312],{},[19,1310,1311],{},"Start countdown if control message is received while not running",": Enable this to start the countdown using a control message, enable it for this example to allow starting countdown when control message recived",[42,1314,1315,1316,1318],{},"Connect the Change node to the ",[19,1317,1266],{}," node.",[42,1320,1321,1322,1325],{},"Add a ",[19,1323,1324],{},"Debug"," node and connect it to the Countdown node output.",[42,1327,297,1328,1331],{},[19,1329,1330],{},"Deploy"," to activate the flow.",[15,1333,1334,1335,1338,1339,1342],{},"When you trigger the Inject node, the ",[19,1336,1337],{},"countdown starts",". The Countdown node outputs messages at regular intervals, and the ",[19,1340,1341],{},"second output"," shows the remaining time.",[15,1344,1345,1349],{},[165,1346],{"alt":1347,"dataZoomable":103,"src":1348},"Countdown node output showing remaining time updates","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fcountdown.png",[170,1350,1347],{},[15,1352,1353],{},"From the first output, it will send the message you configured at the start of the count and at the end of the countdown.",[15,1355,1356],{},"Below is the flow implementation that you can use as a starting point.",[15,1358,1105,1359,1150],{},[1107,1360,1361,1362,1365,1366,1369,1370,1372,1373,1375,1376,1378,1379,1382,1383,1385],{},"{\"id\":\"13e3937b0555d34d\",\"type\":\"group\",\"z\":\"b446dfa04d79d359\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,1363,1364],{},"\"0a7fdceb7b844c76\",\"b1d36b608bafb531\",\"e7e0894b2832e93e\",\"52d0abe15e3533c8\",\"c48dcaec998440aa\"",",\"x\":134,\"y\":3419,\"w\":692,\"h\":122},{\"id\":\"0a7fdceb7b844c76\",\"type\":\"countdown\",\"z\":\"b446dfa04d79d359\",\"g\":\"13e3937b0555d34d\",\"name\":\"\",\"topic\":\"\",\"payloadTimerStart\":\"true\",\"payloadTimerStartType\":\"bool\",\"payloadTimerStop\":\"false\",\"payloadTimerStopType\":\"bool\",\"timer\":\"30\",\"resetWhileRunning\":false,\"outputOnReset\":false,\"setTimeToNewWhileRunning\":false,\"startCountdownOnControlMessage\":true,\"x\":550,\"y\":3480,\"wires\":[[\"e7e0894b2832e93e\"],",[1107,1367,1368],{},"\"52d0abe15e3533c8\"","]},{\"id\":\"b1d36b608bafb531\",\"type\":\"inject\",\"z\":\"b446dfa04d79d359\",\"g\":\"13e3937b0555d34d\",\"name\":\"Start\",\"props\":",[1107,1371],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":230,\"y\":3480,\"wires\":[[\"c48dcaec998440aa\"]]},{\"id\":\"e7e0894b2832e93e\",\"type\":\"debug\",\"z\":\"b446dfa04d79d359\",\"g\":\"13e3937b0555d34d\",\"name\":\"Result\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":690,\"y\":3460,\"wires\":",[1107,1374],{},"},{\"id\":\"52d0abe15e3533c8\",\"type\":\"debug\",\"z\":\"b446dfa04d79d359\",\"g\":\"13e3937b0555d34d\",\"name\":\"Countdown\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":710,\"y\":3500,\"wires\":",[1107,1377],{},"},{\"id\":\"c48dcaec998440aa\",\"type\":\"change\",\"z\":\"b446dfa04d79d359\",\"g\":\"13e3937b0555d34d\",\"name\":\"Set Countdown\",\"rules\":",[1107,1380,1381],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"50\",\"tot\":\"num\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"control\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":380,\"y\":3480,\"wires\":[[\"0a7fdceb7b844c76\"]]},{\"id\":\"66be08ead79304bf\",\"type\":\"global-config\",\"env\":",[1107,1384],{},",\"modules\":{\"node-red-contrib-countdown\":\"1.3.2\"}}",[15,1387,1388,1389,1392,1393,853,1397,1401,1402,1405],{},"For this demonstration, we used an inject node to manually trigger the countdown. In production environments, you would replace this with actual process triggers, ",[19,1390,1391],{},"equipment status signals"," from ",[63,1394,1396],{"href":1395},"\u002Fblog\u002F2025\u002F10\u002Fplc-to-mqtt-using-flowfuse\u002F","PLCs",[63,1398,1400],{"href":1399},"\u002Fblog\u002F2024\u002F06\u002Fhow-to-use-mqtt-in-node-red\u002F","MQTT"," messages from sensors, or completion signals from upstream operations. For example, you might start a ",[19,1403,1404],{},"maintenance window countdown"," when equipment reaches a scheduled service interval, or initiate a cleanup cycle timer when a batch process completes.",[15,1407,1408,1409,1413,1414,1418,1419,29],{},"While these examples cover the essential timer operations, both nodes offer additional configuration options. For comprehensive documentation, visit the ",[63,1410,886],{"href":1411,"rel":1412},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-hourglass",[67]," and ",[63,1415,1163],{"href":1416,"rel":1417},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-countdown",[67]," pages on the ",[19,1420,1421],{},"Node-RED flows library",[877,1423,1425],{"id":1424},"conclusion","Conclusion",[15,1427,1428,1429,1413,1431,1433],{},"That's it, you now have working ",[19,1430,1070],{},[19,1432,890],{}," in Node-RED. The hourglass node tracks elapsed time, and the countdown node triggers actions after a set duration.",[15,1435,1436,1437,1439],{},"These implementations are ready for production use. Start with these basic patterns, test them in your environment, and expand as needed. Connect them to your ",[19,1438,1396],{},", add dashboard displays, or integrate with your existing automation workflows.",[15,1441,1442],{},"Both nodes are actively maintained and used in real industrial systems, so you're building on proven foundations.",{"title":103,"searchDepth":104,"depth":104,"links":1444},[1445,1448,1451],{"id":819,"depth":107,"text":820,"children":1446},[1447],{"id":897,"depth":294,"text":898},{"id":938,"depth":107,"text":939,"children":1449},[1450],{"id":984,"depth":294,"text":985},{"id":1153,"depth":107,"text":1154,"children":1452},[1453,1454],{"id":1167,"depth":104,"text":1168},{"id":1200,"depth":294,"text":1201},"2025-12-24","Node-RED timer tutorial showing how to implement stopwatch and countdown timers for industrial automation and IoT applications.","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fnode-red-timer.png","2026-06-19",{"keywords":1460,"excerpt":1461},"node-red timers, node-red stopwatch, node-red countdown timer, node-red industrial automation, node-red iiot, node-red process control, node-red hourglass, node-red contrib countdown, flowfuse node-red",{"type":12,"value":1462},[1463],[15,1464,776],{},"\u002Fblog\u002F2025\u002F12\u002Fnode-red-timer",{"title":770,"description":1456},{"loc":1465},"blog\u002F2025\u002F12\u002Fnode-red-timer","Implement stopwatch and countdown timers for industrial automation and process control applications",[138],"Timers are foundational to industrial automation, used for tracking machine runtime, measuring downtime, scheduling maintenance, and coordinating sequential processes. This tutorial shows how to implement both stopwatch timers and countdown timers in Node-RED using dedicated nodes, with practical examples suited for production floor displays and operator terminals.","C9gA6rvNMCXz7JgEbu4VhAH1G8ty_Ycwha4mW7EWjBQ",{"id":1474,"title":1475,"authors":1476,"body":1477,"cta":3,"date":2601,"description":2602,"extension":116,"image":2603,"lastUpdated":1458,"meta":2604,"navigation":129,"path":2610,"seo":2611,"sitemap":2612,"stem":2613,"subtitle":2614,"tags":2615,"tldr":2616,"video":3,"__hash__":2617},"blog\u002Fblog\u002F2025\u002F12\u002Fgetting-weather-data-in-node-red.md","Building a Weather Dashboard in Node-RED (2026)",[144],{"type":12,"value":1478,"toc":2585},[1479,1482,1485,1488,1492,1495,1513,1517,1520,1541,1554,1558,1561,1575,1579,1582,1613,1621,1681,1689,1694,1709,1723,1728,1731,1734,1738,1745,1755,2085,2101,2113,2121,2125,2131,2135,2138,2142,2145,2182,2190,2193,2197,2200,2228,2236,2239,2243,2246,2302,2310,2313,2317,2320,2355,2363,2366,2370,2373,2455,2463,2467,2470,2473,2481,2487,2565,2569,2572,2575,2578,2581],[15,1480,1481],{},"A weather dashboard is honestly the best first project if you're getting into Node-RED. Takes about 10-15 minutes from start to finish, and by the end you'll understand how the whole thing works - connecting to APIs, processing data, and displaying it visually.",[15,1483,1484],{},"This isn't one of our typical deep-dive industrial posts, it's a straightforward starter tutorial. You'll be building something real: calling an actual weather API, handling JSON responses, and watching live data appear on your dashboard. It's the kind of project that makes Node-RED's flow-based approach suddenly make sense. Once you've built it, you'll have a solid foundation for more complex projects.",[15,1486,1487],{},"We'll use FlowFuse Dashboard for the UI since it's modern and easier to work with. If you know how to drag nodes around and hit the deploy button, you're ready to start.",[31,1489,1491],{"id":1490},"what-youll-need","What You'll Need",[15,1493,1494],{},"Before you start, make sure you have:",[39,1496,1497,1503],{},[42,1498,1499,1502],{},[19,1500,1501],{},"Node-RED Instance:"," You need Node-RED running somewhere. Easiest option is FlowFuse, [grab a free trial]({% include \"sign-up-url.njk\" %}) and you get a cloud-hosted instance ready to go. No server setup, no port forwarding hassles.",[42,1504,1505,1508,1509,1512],{},[19,1506,1507],{},"OpenWeatherMap Account:"," Sign up at ",[924,1510,1511],{},"openweathermap.org",". The free tier gives you enough API calls for this project.",[31,1514,1516],{"id":1515},"installing-flowfuse-dashboard","Installing FlowFuse Dashboard",[15,1518,1519],{},"First, get the dashboard package installed:",[271,1521,1522,1525,1528,1531,1538],{},[42,1523,1524],{},"Click the hamburger menu in the top right corner",[42,1526,1527],{},"Select Manage palette",[42,1529,1530],{},"Go to the Install tab",[42,1532,1533,1534,1537],{},"Type ",[924,1535,1536],{},"@flowfuse\u002Fnode-red-dashboard"," in the search box",[42,1539,1540],{},"Click the install button next to it",[15,1542,1543,1544,853,1547,853,1550,1553],{},"Wait for it to finish. You'll see a bunch of new dashboard nodes pop up in your left sidebar, things like ",[19,1545,1546],{},"ui-gauge",[19,1548,1549],{},"ui-text",[19,1551,1552],{},"ui-chart",". That's how you know it worked.",[31,1555,1557],{"id":1556},"getting-your-api-key","Getting Your API Key",[15,1559,1560],{},"Log in to your OpenWeather account. Once you're signed in:",[271,1562,1563,1566,1569,1572],{},[42,1564,1565],{},"Go to your account section",[42,1567,1568],{},"Find API keys",[42,1570,1571],{},"Copy the default key (or generate a new one)",[42,1573,1574],{},"Save it in a text file or a note app.",[31,1576,1578],{"id":1577},"setting-up-the-api-connection","Setting Up the API Connection",[15,1580,1581],{},"First, we need to connect to the weather API and make sure it's working.",[271,1583,1584,1590,1593,1600,1603,1610],{},[42,1585,1045,1586,1589],{},[19,1587,1588],{},"inject"," node onto the canvas",[42,1591,1592],{},"Double-click to configure it",[42,1594,1595,1596,1599],{},"Change ",[19,1597,1598],{},"Repeat"," from \"none\" to \"interval\"",[42,1601,1602],{},"Set it to repeat every 5 seconds (or whatever interval you prefer)",[42,1604,1605,1606,1609],{},"Check the box for ",[19,1607,1608],{},"Inject once after"," and set it to 0.1 seconds, this will trigger the flow immediately when you deploy",[42,1611,1612],{},"Click Done",[15,1614,1615,1619],{},[165,1616],{"alt":1617,"dataZoomable":103,"src":1618},"Inject node configured to trigger the weather API every 5 seconds","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Ftrigger-weather-api.png",[170,1620,1617],{},[271,1622,1624,1630,1639,1645,1652,1662,1669,1675],{"start":1623},7,[42,1625,1045,1626,1629],{},[19,1627,1628],{},"http request"," node to the right of it",[42,1631,1263,1632,1635,1636,1638],{},[19,1633,1634],{},"debug"," node to the right of the ",[19,1637,1628],{}," node",[42,1640,1641,1642,1644],{},"Double-click the ",[19,1643,1628],{}," node to open its settings:",[42,1646,1647,1648,1651],{},"Make sure ",[19,1649,1650],{},"Method"," is set to GET",[42,1653,1654,1655,1658,1659],{},"In the ",[19,1656,1657],{},"URL"," field, paste: ",[924,1660,1661],{},"https:\u002F\u002Fapi.openweathermap.org\u002Fdata\u002F2.5\u002Fweather?q=London&appid=YOUR_API_KEY&units=metric",[42,1663,1664,1665,1668],{},"Replace ",[924,1666,1667],{},"YOUR_API_KEY"," with the actual API key you copied from OpenWeatherMap",[42,1670,1664,1671,1674],{},[924,1672,1673],{},"London"," with your city if you want",[42,1676,1677,1678,29],{},"Select the Return as ",[19,1679,1680],{},"parsed JSON",[15,1682,1683,1687],{},[165,1684],{"alt":1685,"dataZoomable":103,"src":1686},"HTTP request node configured to fetch weather data from OpenWeatherMap API","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fhttp-request.png",[170,1688,1685],{},[271,1690,1692],{"start":1691},14,[42,1693,1612],{},[15,1695,175,1696,1699,1700,1703,1704,29],{},[924,1697,1698],{},"units=metric"," gives you Celsius. Change it to ",[924,1701,1702],{},"units=imperial"," for Fahrenheit. For more details on what parameters you can use, check out the ",[63,1705,1708],{"href":1706,"rel":1707},"https:\u002F\u002Fopenweathermap.org\u002Fcurrent",[67],"OpenWeatherMap API documentation",[15,1710,1711,1712,1714,1715,1717,1718,1720,1721,1318],{},"Now wire the nodes together by clicking and dragging from the ",[19,1713,1588],{}," node's right side to the ",[19,1716,1628],{}," node's left side, then from the ",[19,1719,1628],{}," node to the ",[19,1722,1634],{},[15,1724,278,1725,1727],{},[19,1726,1330],{}," button in the top right and open the debug panel on the right sidebar if it's not already open.",[15,1729,1730],{},"You should see a JSON object with weather data, temperature, humidity, wind speed, description, and more. This is the raw data coming back from the API.",[15,1732,1733],{},"If you see a 401 error, your API key may still be activating. Wait 10–15 minutes and try again, or verify the key again in case it’s invalid or mistyped.",[31,1735,1737],{"id":1736},"processing-the-weather-data","Processing the Weather Data",[15,1739,1740,1741,1744],{},"Now that you're getting data from the API, you need to extract the specific values you want to display. We'll use a ",[19,1742,1743],{},"function"," node to pull out temperature, humidity, weather description, and wind speed.",[271,1746,1747],{},[42,1748,1263,1749,1751,1752,1754],{},[19,1750,1743],{}," node and add following code into it and connect it to the ",[19,1753,1628],{}," node:",[1756,1757,1761],"pre",{"className":1758,"code":1759,"language":1760,"meta":103,"style":103},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Extract the data we need\nconst temp = msg.payload.main.temp;\nconst humidity = msg.payload.main.humidity;\nconst description = msg.payload.weather[0].description;\nconst windSpeed = msg.payload.wind.speed;\nconst city = msg.payload.name;\n\n\u002F\u002F Create separate messages for each value\nreturn [\n    { payload: city, topic: \"city\" },\n    { payload: description, topic: \"description\" },\n    { payload: temp, topic: \"temperature\" },\n    { payload: humidity, topic: \"humidity\" },\n    { payload: windSpeed, topic: \"wind\" },\n];\n","javascript",[924,1762,1763,1771,1806,1832,1866,1894,1917,1922,1928,1938,1974,2000,2027,2053,2078],{"__ignoreMap":103},[1107,1764,1767],{"class":1765,"line":1766},"line",1,[1107,1768,1770],{"class":1769},"sHwdD","\u002F\u002F Extract the data we need\n",[1107,1772,1773,1777,1781,1785,1788,1790,1793,1795,1798,1800,1803],{"class":1765,"line":107},[1107,1774,1776],{"class":1775},"spNyl","const",[1107,1778,1780],{"class":1779},"sTEyZ"," temp ",[1107,1782,1784],{"class":1783},"sMK4o","=",[1107,1786,1787],{"class":1779}," msg",[1107,1789,29],{"class":1783},[1107,1791,1792],{"class":1779},"payload",[1107,1794,29],{"class":1783},[1107,1796,1797],{"class":1779},"main",[1107,1799,29],{"class":1783},[1107,1801,1802],{"class":1779},"temp",[1107,1804,1805],{"class":1783},";\n",[1107,1807,1808,1810,1813,1815,1817,1819,1821,1823,1825,1827,1830],{"class":1765,"line":294},[1107,1809,1776],{"class":1775},[1107,1811,1812],{"class":1779}," humidity ",[1107,1814,1784],{"class":1783},[1107,1816,1787],{"class":1779},[1107,1818,29],{"class":1783},[1107,1820,1792],{"class":1779},[1107,1822,29],{"class":1783},[1107,1824,1797],{"class":1779},[1107,1826,29],{"class":1783},[1107,1828,1829],{"class":1779},"humidity",[1107,1831,1805],{"class":1783},[1107,1833,1834,1836,1839,1841,1843,1845,1847,1849,1852,1856,1859,1861,1864],{"class":1765,"line":104},[1107,1835,1776],{"class":1775},[1107,1837,1838],{"class":1779}," description ",[1107,1840,1784],{"class":1783},[1107,1842,1787],{"class":1779},[1107,1844,29],{"class":1783},[1107,1846,1792],{"class":1779},[1107,1848,29],{"class":1783},[1107,1850,1851],{"class":1779},"weather[",[1107,1853,1855],{"class":1854},"sbssI","0",[1107,1857,1858],{"class":1779},"]",[1107,1860,29],{"class":1783},[1107,1862,1863],{"class":1779},"description",[1107,1865,1805],{"class":1783},[1107,1867,1869,1871,1874,1876,1878,1880,1882,1884,1887,1889,1892],{"class":1765,"line":1868},5,[1107,1870,1776],{"class":1775},[1107,1872,1873],{"class":1779}," windSpeed ",[1107,1875,1784],{"class":1783},[1107,1877,1787],{"class":1779},[1107,1879,29],{"class":1783},[1107,1881,1792],{"class":1779},[1107,1883,29],{"class":1783},[1107,1885,1886],{"class":1779},"wind",[1107,1888,29],{"class":1783},[1107,1890,1891],{"class":1779},"speed",[1107,1893,1805],{"class":1783},[1107,1895,1897,1899,1902,1904,1906,1908,1910,1912,1915],{"class":1765,"line":1896},6,[1107,1898,1776],{"class":1775},[1107,1900,1901],{"class":1779}," city ",[1107,1903,1784],{"class":1783},[1107,1905,1787],{"class":1779},[1107,1907,29],{"class":1783},[1107,1909,1792],{"class":1779},[1107,1911,29],{"class":1783},[1107,1913,1914],{"class":1779},"name",[1107,1916,1805],{"class":1783},[1107,1918,1919],{"class":1765,"line":1623},[1107,1920,1921],{"emptyLinePlaceholder":129},"\n",[1107,1923,1925],{"class":1765,"line":1924},8,[1107,1926,1927],{"class":1769},"\u002F\u002F Create separate messages for each value\n",[1107,1929,1931,1935],{"class":1765,"line":1930},9,[1107,1932,1934],{"class":1933},"s7zQu","return",[1107,1936,1937],{"class":1779}," [\n",[1107,1939,1941,1944,1948,1950,1953,1956,1959,1961,1964,1968,1971],{"class":1765,"line":1940},10,[1107,1942,1943],{"class":1783},"    {",[1107,1945,1947],{"class":1946},"swJcz"," payload",[1107,1949,992],{"class":1783},[1107,1951,1952],{"class":1779}," city",[1107,1954,1955],{"class":1783},",",[1107,1957,1958],{"class":1946}," topic",[1107,1960,992],{"class":1783},[1107,1962,1963],{"class":1783}," \"",[1107,1965,1967],{"class":1966},"sfazB","city",[1107,1969,1970],{"class":1783},"\"",[1107,1972,1973],{"class":1783}," },\n",[1107,1975,1977,1979,1981,1983,1986,1988,1990,1992,1994,1996,1998],{"class":1765,"line":1976},11,[1107,1978,1943],{"class":1783},[1107,1980,1947],{"class":1946},[1107,1982,992],{"class":1783},[1107,1984,1985],{"class":1779}," description",[1107,1987,1955],{"class":1783},[1107,1989,1958],{"class":1946},[1107,1991,992],{"class":1783},[1107,1993,1963],{"class":1783},[1107,1995,1863],{"class":1966},[1107,1997,1970],{"class":1783},[1107,1999,1973],{"class":1783},[1107,2001,2003,2005,2007,2009,2012,2014,2016,2018,2020,2023,2025],{"class":1765,"line":2002},12,[1107,2004,1943],{"class":1783},[1107,2006,1947],{"class":1946},[1107,2008,992],{"class":1783},[1107,2010,2011],{"class":1779}," temp",[1107,2013,1955],{"class":1783},[1107,2015,1958],{"class":1946},[1107,2017,992],{"class":1783},[1107,2019,1963],{"class":1783},[1107,2021,2022],{"class":1966},"temperature",[1107,2024,1970],{"class":1783},[1107,2026,1973],{"class":1783},[1107,2028,2030,2032,2034,2036,2039,2041,2043,2045,2047,2049,2051],{"class":1765,"line":2029},13,[1107,2031,1943],{"class":1783},[1107,2033,1947],{"class":1946},[1107,2035,992],{"class":1783},[1107,2037,2038],{"class":1779}," humidity",[1107,2040,1955],{"class":1783},[1107,2042,1958],{"class":1946},[1107,2044,992],{"class":1783},[1107,2046,1963],{"class":1783},[1107,2048,1829],{"class":1966},[1107,2050,1970],{"class":1783},[1107,2052,1973],{"class":1783},[1107,2054,2055,2057,2059,2061,2064,2066,2068,2070,2072,2074,2076],{"class":1765,"line":1691},[1107,2056,1943],{"class":1783},[1107,2058,1947],{"class":1946},[1107,2060,992],{"class":1783},[1107,2062,2063],{"class":1779}," windSpeed",[1107,2065,1955],{"class":1783},[1107,2067,1958],{"class":1946},[1107,2069,992],{"class":1783},[1107,2071,1963],{"class":1783},[1107,2073,1886],{"class":1966},[1107,2075,1970],{"class":1783},[1107,2077,1973],{"class":1783},[1107,2079,2081,2083],{"class":1765,"line":2080},15,[1107,2082,1858],{"class":1779},[1107,2084,1805],{"class":1783},[259,2086,2087],{},[15,2088,2089,2092,2093,2095,2096,2100],{},[19,2090,2091],{},"Tip:"," If you're using FlowFuse, you don't need to write this JavaScript manually. You can use the FlowFuse Expert to generate the function code for you, just describe what you want the ",[19,2094,1743],{}," node to do. Check out the ",[63,2097,2099],{"href":2098},"\u002Fblog\u002F2025\u002F07\u002Fflowfuse-ai-assistant-better-node-red-manufacturing\u002F","article"," for more details.",[271,2102,2103],{"start":107},[42,2104,2105,2106,2108,2109,2112],{},"Set the ",[19,2107,1743],{}," node's ",[19,2110,2111],{},"Outputs"," (in the Setup tab) to 5, since the function will return five separate messages.",[15,2114,2115,2119],{},[165,2116],{"alt":2117,"dataZoomable":103,"src":2118},"Function node configured with 5 outputs to split weather data","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Ffunction-setup-tab.png",[170,2120,2117],{},[271,2122,2123],{"start":294},[42,2124,1612],{},[15,2126,2127,2128,2130],{},"This ",[19,2129,1743],{}," node splits the API response into separate outputs - one for temperature, one for humidity, and so on. Each output gets its own topic label so you can track what's what.",[31,2132,2134],{"id":2133},"building-the-dashboard","Building the Dashboard",[15,2136,2137],{},"Now you'll see your weather data displayed on screen. This demonstrates how Node-RED connects data sources to visual components. We'll organize the dashboard into separate groups for better visual organization.",[399,2139,2141],{"id":2140},"configuring-the-city-name-display","Configuring the City Name Display",[15,2143,2144],{},"Start by displaying which city you're tracking:",[271,2146,2147,2151,2161,2166,2172,2179],{},[42,2148,1263,2149,1589],{},[19,2150,1549],{},[42,2152,2153,2154,2157,2158,2160],{},"Connect it to ",[19,2155,2156],{},"output 1"," of your ",[19,2159,1743],{}," node (the city output)",[42,2162,1641,2163,2165],{},[19,2164,1549],{}," node to open its settings",[42,2167,1224,2168,2171],{},[19,2169,2170],{},"Label"," to \"City\"",[42,2173,2174,2175,2178],{},"For ",[19,2176,2177],{},"Group",", click the pencil icon to create a new group called \"Weather Info\"",[42,2180,2181],{},"Click Add, then Done",[15,2183,2184,2188],{},[165,2185],{"alt":2186,"dataZoomable":103,"src":2187},"UI text node configured to display city name in Weather Info group","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fcity-display.png",[170,2189,2186],{},[15,2191,2192],{},"You've just created your first dashboard element and dashboard group. Groups organize widgets on the page and function as containers.",[399,2194,2196],{"id":2195},"configuring-the-weather-description","Configuring the Weather Description",[15,2198,2199],{},"Add a text field to show current conditions:",[271,2201,2202,2207,2215,2218,2223,2226],{},[42,2203,2204,2205,1589],{},"Drag another ",[19,2206,1549],{},[42,2208,2153,2209,2157,2212,2214],{},[19,2210,2211],{},"output 2",[19,2213,1743],{}," node (description output)",[42,2216,2217],{},"Double-click to configure",[42,2219,1224,2220,2222],{},[19,2221,2170],{}," to \"Conditions\"",[42,2224,2225],{},"Use the same \"Weather Info\" group",[42,2227,1612],{},[15,2229,2230,2234],{},[165,2231],{"alt":2232,"dataZoomable":103,"src":2233},"UI text node configured to display weather conditions","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fcondition-display.png",[170,2235,2232],{},[15,2237,2238],{},"The API returns descriptions like \"scattered clouds\" or \"light rain\" - human-readable conditions.",[399,2240,2242],{"id":2241},"configuring-the-temperature-gauge","Configuring the Temperature Gauge",[15,2244,2245],{},"Next, add a circular gauge for temperature:",[271,2247,2248,2252,2260,2263,2268,2278,2281,2290,2293,2300],{},[42,2249,1263,2250,1589],{},[19,2251,1546],{},[42,2253,2153,2254,2157,2257,2259],{},[19,2255,2256],{},"output 3",[19,2258,1743],{}," node (the temperature output)",[42,2261,2262],{},"Double-click to open settings",[42,2264,1224,2265,2267],{},[19,2266,2170],{}," to \"Temperature (°C)\"",[42,2269,1224,2270,2273,2274,2277],{},[19,2271,2272],{},"Min"," to 0 and ",[19,2275,2276],{},"Max"," to 50 (adjust these based on your climate)",[42,2279,2280],{},"Add the color segments you want, as shown in the reference image.",[42,2282,2174,2283,2285,2286,2289],{},[19,2284,2177],{},", click the pencil icon to create a ",[19,2287,2288],{},"new group"," called \"Temperature & Humidity\"",[42,2291,2292],{},"Click Add to create the group",[42,2294,2295,2296,2299],{},"Under the ",[19,2297,2298],{},"Appearance"," tab, you can select a color scheme if desired",[42,2301,1612],{},[15,2303,2304,2308],{},[165,2305],{"alt":2306,"dataZoomable":103,"src":2307},"Temperature gauge configured with color-coded segments in Temperature & Humidity group","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Ftemperature-gauge.png",[170,2309,2306],{},[15,2311,2312],{},"The gauge will automatically color-code based on the value - cooler temperatures display in blue tones, warmer in orange\u002Fred.",[399,2314,2316],{"id":2315},"configuring-the-humidity-gauge","Configuring the Humidity Gauge",[15,2318,2319],{},"Add the humidity gauge to the same group:",[271,2321,2322,2326,2334,2336,2341,2348,2350,2353],{},[42,2323,2204,2324,1589],{},[19,2325,1546],{},[42,2327,2153,2328,2157,2331,2333],{},[19,2329,2330],{},"output 4",[19,2332,1743],{}," node (humidity output)",[42,2335,2217],{},[42,2337,1224,2338,2340],{},[19,2339,2170],{}," to \"Humidity (%)\"",[42,2342,1224,2343,2273,2345,2347],{},[19,2344,2272],{},[19,2346,2276],{}," to 100 (humidity is always a percentage)",[42,2349,2280],{},[42,2351,2352],{},"Use the same \"Temperature & Humidity\" group from the dropdown",[42,2354,1612],{},[15,2356,2357,2361],{},[165,2358],{"alt":2359,"dataZoomable":103,"src":2360},"Humidity gauge configured to display percentage values with color coding","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fhumidity-gauge.png",[170,2362,2359],{},[15,2364,2365],{},"The two gauges will display side by side in the same group, making it easy to compare both metrics at once.",[31,2367,2369],{"id":2368},"configuring-the-wind-speed-display","Configuring the Wind Speed Display",[15,2371,2372],{},"Next, create a dedicated chart to display wind speed trends over time:",[271,2374,2375,2380,2388,2393,2400,2408,2418,2426,2433,2445],{},[42,2376,1263,2377,2379],{},[19,2378,1552],{}," node onto your flow.",[42,2381,2153,2382,2157,2385,2387],{},[19,2383,2384],{},"output 5",[19,2386,1743],{}," node (the wind speed output).",[42,2389,1641,2390,2392],{},[19,2391,1552],{}," node to configure it.",[42,2394,1224,2395,1242,2397,29],{},[19,2396,2170],{},[19,2398,2399],{},"\"Wind Speed (m\u002Fs)\"",[42,2401,1224,2402,1242,2405,29],{},[19,2403,2404],{},"Type",[19,2406,2407],{},"Line chart",[42,2409,2410,2411,2414,2415,29],{},"For the ",[19,2412,2413],{},"X-axis",", select ",[19,2416,2417],{},"Timestamp",[42,2419,1224,2420,2423,2424,29],{},[19,2421,2422],{},"Y-axis"," to use ",[19,2425,1227],{},[42,2427,1224,2428,1242,2431,29],{},[19,2429,2430],{},"Series",[19,2432,1241],{},[42,2434,2435,2436,2438,2439,2441,2442,29],{},"Under ",[19,2437,2177],{},", click the pencil icon and create a ",[19,2440,2288],{}," named ",[19,2443,2444],{},"\"Wind Speed\"",[42,2446,297,2447,2450,2451,2454],{},[19,2448,2449],{},"Add",", then ",[19,2452,2453],{},"Done"," to save the configuration.",[15,2456,2457,2461],{},[165,2458],{"alt":2459,"dataZoomable":103,"src":2460},"Wind speed chart configured as a line chart to show trends over time","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fwind-speed-chart.png",[170,2462,2459],{},[31,2464,2466],{"id":2465},"viewing-your-dashboard","Viewing Your Dashboard",[15,2468,2469],{},"Hit the Deploy button in the top right corner.",[15,2471,2472],{},"Then, open the Dashboard 2.0 sidebar and click the Open Dashboard button. You should now see a dashboard similar to the one shown below.",[15,2474,2475,2479],{},[165,2476],{"alt":2477,"dataZoomable":103,"src":2478},"Complete weather dashboard displaying real-time weather data","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fweather-dashboard.png",[170,2480,2477],{},[15,2482,2483,2484,2486],{},"Below is the complete flow. Import it, enter your API URL with your API key in the ",[19,2485,1628],{}," node, and deploy the flow:",[15,2488,1105,2489,1150],{},[1107,2490,2491,2492,2494,2495,2497,2498,2500,2501,2504,2505,2507,2508,1955,2511,1955,2514,1955,2517,2520,2521,2524,2525,2528,2529,2531,2532,2534,2535,2537,2538,2541,2542,2545,2546,2549,2550,2553,2554,2557,2558,2561,2562,2564],{},"{\"id\":\"f4c08591654c29ef\",\"type\":\"inject\",\"z\":\"9cf82b68bb89e8ce\",\"name\":\"Trigger Weather API\",\"props\":",[1107,2493],{},",\"repeat\":\"5\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":100,\"y\":1300,\"wires\":[[\"70d3dab532802c97\"]]},{\"id\":\"70d3dab532802c97\",\"type\":\"http request\",\"z\":\"9cf82b68bb89e8ce\",\"name\":\"OpenWeather Request\",\"method\":\"GET\",\"ret\":\"obj\",\"paytoqs\":\"ignore\",\"url\":\"\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":",[1107,2496],{},",\"x\":340,\"y\":1300,\"wires\":[[\"849a9025bb2f8a78\"]]},{\"id\":\"849a9025bb2f8a78\",\"type\":\"function\",\"z\":\"9cf82b68bb89e8ce\",\"name\":\"Process Weather Data\",\"func\":\"\u002F\u002F Extract the data we need\\nconst temp = msg.payload.main.temp;\\nconst humidity = msg.payload.main.humidity;\\nconst description = msg.payload.weather",[1107,2499,1855],{},".description;\\nconst windSpeed = msg.payload.wind.speed;\\nconst city = msg.payload.name;\\n\\n\u002F\u002F Create separate messages for each value\\nreturn ",[1107,2502,2503],{},"\\n    { payload: city, topic: \"city\" },\\n    { payload: description, topic: \"description\" },\\n    { payload: temp, topic: \"temperature\" },\\n    { payload: humidity, topic: \"humidity\" },\\n    { payload: windSpeed, topic: \"wind\" },\\n",";\",\"outputs\":5,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":",[1107,2506],{},",\"x\":580,\"y\":1300,\"wires\":[[\"b32ec3dc036c981e\"],",[1107,2509,2510],{},"\"d6a1cfaa374bf3e9\"",[1107,2512,2513],{},"\"5313d0217f55e2cf\"",[1107,2515,2516],{},"\"fca37c757fb4d69d\"",[1107,2518,2519],{},"\"b668bf185e686847\"","]},{\"id\":\"5313d0217f55e2cf\",\"type\":\"ui-gauge\",\"z\":\"9cf82b68bb89e8ce\",\"name\":\"Temperature\",\"group\":\"9470e51a8ec86d8e\",\"order\":1,\"value\":\"payload\",\"valueType\":\"msg\",\"width\":\"3\",\"height\":\"3\",\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"Temperature\",\"alwaysShowTitle\":false,\"floatingTitlePosition\":\"top-left\",\"units\":\"°C\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":",[1107,2522,2523],{},"{\"from\":\"0\",\"color\":\"#008cb4\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"15\",\"color\":\"#00a3d7\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"25\",\"color\":\"#fec700\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"35\",\"color\":\"#ffaa00\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"50\",\"color\":\"#ff6251\",\"text\":\"\",\"textType\":\"value\"}",",\"min\":0,\"max\":\"50\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":790,\"y\":1300,\"wires\":[[]]},{\"id\":\"b32ec3dc036c981e\",\"type\":\"ui-text\",\"z\":\"9cf82b68bb89e8ce\",\"group\":\"5a89ac7171f51cc3\",\"order\":1,\"width\":\"0\",\"height\":\"0\",\"name\":\"City Display\",\"label\":\"City:\",\"format\":\"",[2526,2527],"binding",{"value":1227},"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Arial,Arial,Helvetica,sans-serif\",\"fontSize\":\"21\",\"color\":\"#000000\",\"wrapText\":false,\"className\":\"\",\"value\":\"payload\",\"valueType\":\"msg\",\"x\":790,\"y\":1220,\"wires\":",[1107,2530],{},"},{\"id\":\"d6a1cfaa374bf3e9\",\"type\":\"ui-text\",\"z\":\"9cf82b68bb89e8ce\",\"group\":\"5a89ac7171f51cc3\",\"order\":3,\"width\":\"0\",\"height\":\"0\",\"name\":\"Conditions Display\",\"label\":\"Conditions:\",\"format\":\"",[2526,2533],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Arial,Arial,Helvetica,sans-serif\",\"fontSize\":\"21\",\"color\":\"#000000\",\"wrapText\":false,\"className\":\"\",\"value\":\"payload\",\"valueType\":\"msg\",\"x\":810,\"y\":1260,\"wires\":",[1107,2536],{},"},{\"id\":\"fca37c757fb4d69d\",\"type\":\"ui-gauge\",\"z\":\"9cf82b68bb89e8ce\",\"name\":\"Humidity Gauge\",\"group\":\"9470e51a8ec86d8e\",\"order\":2,\"value\":\"payload\",\"valueType\":\"msg\",\"width\":\"3\",\"height\":\"3\",\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"Humidity\",\"alwaysShowTitle\":false,\"floatingTitlePosition\":\"top-left\",\"units\":\"%\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":",[1107,2539,2540],{},"{\"from\":\"0\",\"color\":\"#d95000\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"30\",\"color\":\"#6f7608\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"80\",\"color\":\"#fec700\",\"text\":\"\",\"textType\":\"value\"},{\"from\":\"100\",\"color\":\"#d95000\",\"text\":\"\",\"textType\":\"value\"}",",\"min\":0,\"max\":\"100\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":800,\"y\":1340,\"wires\":[[]]},{\"id\":\"b668bf185e686847\",\"type\":\"ui-chart\",\"z\":\"9cf82b68bb89e8ce\",\"group\":\"7b2025c104cd506e\",\"name\":\"Wind Speed\",\"label\":\"Wind Speed (m\u002Fs)\",\"order\":1,\"chartType\":\"line\",\"category\":\"topic\",\"categoryType\":\"msg\",\"xAxisLabel\":\"\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"timestamp\",\"xAxisType\":\"time\",\"xAxisFormat\":\"\",\"xAxisFormatType\":\"auto\",\"xmin\":\"\",\"xmax\":\"\",\"yAxisLabel\":\"\",\"yAxisProperty\":\"payload\",\"yAxisPropertyType\":\"msg\",\"ymin\":\"\",\"ymax\":\"\",\"bins\":10,\"action\":\"append\",\"stackSeries\":false,\"pointShape\":\"circle\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":",[1107,2543,2544],{},"\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#a347e1\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"",",\"textColor\":",[1107,2547,2548],{},"\"#666666\"",",\"textColorDefault\":true,\"gridColor\":",[1107,2551,2552],{},"\"#e5e5e5\"",",\"gridColorDefault\":true,\"width\":\"12\",\"height\":\"4\",\"className\":\"\",\"interpolation\":\"linear\",\"x\":790,\"y\":1380,\"wires\":[[]]},{\"id\":\"9470e51a8ec86d8e\",\"type\":\"ui-group\",\"name\":\"Temperature & Humidity\",\"page\":\"f1eb99b1e714d411\",\"width\":6,\"height\":1,\"order\":2,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"5a89ac7171f51cc3\",\"type\":\"ui-group\",\"name\":\"Weather Info\",\"page\":\"f1eb99b1e714d411\",\"width\":\"6\",\"height\":\"4\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"7b2025c104cd506e\",\"type\":\"ui-group\",\"name\":\"Wind Speed\",\"page\":\"f1eb99b1e714d411\",\"width\":\"12\",\"height\":1,\"order\":3,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"f1eb99b1e714d411\",\"type\":\"ui-page\",\"name\":\"Weather Dashboard\",\"ui\":\"afea04ce8735c0a6\",\"path\":\"\u002Fweather\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"93822a7b43673c58\",\"breakpoints\":",[1107,2555,2556],{},"{\"name\":\"Default\",\"px\":\"0\",\"cols\":\"3\"},{\"name\":\"Tablet\",\"px\":\"576\",\"cols\":\"6\"},{\"name\":\"Small Desktop\",\"px\":\"768\",\"cols\":\"9\"},{\"name\":\"Desktop\",\"px\":\"1024\",\"cols\":\"12\"}",",\"order\":1,\"className\":\"\",\"visible\":true,\"disabled\":false},{\"id\":\"afea04ce8735c0a6\",\"type\":\"ui-base\",\"name\":\"UI Name\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,2559,2560],{},"\"ui-control\",\"ui-notification\"",",\"headerContent\":\"page\",\"titleBarStyle\":\"default\",\"showReconnectNotification\":true,\"notificationDisplayTime\":5,\"showDisconnectNotification\":true,\"allowInstall\":true},{\"id\":\"93822a7b43673c58\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#00a3d7\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"density\":\"default\",\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"950b3b399ea22ea6\",\"type\":\"global-config\",\"env\":",[1107,2563],{},",\"modules\":{\"@flowfuse\u002Fnode-red-dashboard\":\"1.29.0\"}}",[31,2566,2568],{"id":2567},"whats-next","What's Next?",[15,2570,2571],{},"That's it! You've built a real-time weather dashboard and learned the basics of Node-RED, connecting to APIs, processing data, and building visual interfaces.",[15,2573,2574],{},"Throughout this tutorial, you used FlowFuse to host Node-RED and FlowFuse Dashboard for the UI. If you're just starting out, FlowFuse makes things easier, no server setup, no port forwarding, and your dashboard works anywhere. Plus, when you're ready to build bigger projects, features like team collaboration, DevOps pipelines, RBAC, snapshots, and audit logs are already built in.",[15,2576,2577],{},"Try expanding your dashboard by adding more cities, creating historical charts, or setting up weather alerts. The pattern stays the same, you're just swapping data sources and visualizations.",[15,2579,2580],{},"[Start your free FlowFuse trial]({% include \"sign-up-url.njk\" %}) and keep building.",[2582,2583,2584],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":2586},[2587,2588,2589,2590,2591,2592,2598,2599,2600],{"id":1490,"depth":107,"text":1491},{"id":1515,"depth":107,"text":1516},{"id":1556,"depth":107,"text":1557},{"id":1577,"depth":107,"text":1578},{"id":1736,"depth":107,"text":1737},{"id":2133,"depth":107,"text":2134,"children":2593},[2594,2595,2596,2597],{"id":2140,"depth":294,"text":2141},{"id":2195,"depth":294,"text":2196},{"id":2241,"depth":294,"text":2242},{"id":2315,"depth":294,"text":2316},{"id":2368,"depth":107,"text":2369},{"id":2465,"depth":107,"text":2466},{"id":2567,"depth":107,"text":2568},"2025-12-05","Learn how to build a real-time weather dashboard in Node-RED using the OpenWeather API and FlowFuse Dashboard.","\u002Fblog\u002F2025\u002F12\u002Fimages\u002Fbuilding-a-weather-dashboard.png",{"keywords":2605,"excerpt":2606},"nodered weather, weather dashboard node-red, node-red weather dashboard",{"type":12,"value":2607},[2608],[15,2609,1481],{},"\u002Fblog\u002F2025\u002F12\u002Fgetting-weather-data-in-node-red",{"title":1475,"description":2602},{"loc":2610},"blog\u002F2025\u002F12\u002Fgetting-weather-data-in-node-red","Build a live weather dashboard in Node-RED with FlowFuse",[138],"Building a live weather dashboard in Node-RED is an ideal beginner project that teaches how to connect to REST APIs, process JSON responses, and display data visually. This tutorial uses the free OpenWeatherMap API and FlowFuse Dashboard to create a real-time display with gauges, text, and charts that refreshes on a configurable interval.","6aEDDHts1t7zeFgAM2r1xQqSJgslRK-K6CNzELdJH5s",{"id":2619,"title":2620,"authors":2621,"body":2623,"cta":3,"date":2769,"description":2770,"extension":116,"image":2771,"lastUpdated":3,"meta":2772,"navigation":129,"path":2778,"seo":2779,"sitemap":2780,"stem":2781,"subtitle":2782,"tags":2783,"tldr":3,"video":2785,"__hash__":2786},"blog\u002Fblog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations.md","FlowFuse + LLM + MCP = Text Driven Operations",[2622],"zeger-jan-van-de-weg",{"type":12,"value":2624,"toc":2763},[2625,2628,2631,2635,2652,2659,2662,2666,2669,2676,2683,2697,2705,2712,2716,2719,2722,2725,2728,2732,2739,2745,2748,2751,2754],[15,2626,2627],{},"In industrial operations it's all about getting more out of the CAPEX already\nspent. Achieving higher efficiency means everyone needs to get data from a lot of\ndifferent machines, have an understanding how these machines form lines and fit\ntogether, and holistically understand these as a group of assets that collectively\ncan achieve more.",[15,2629,2630],{},"With the rapid adoption of Artificial Intelligence (AI), Model Context\nProtocol, low-code platforms it's clear that the future of operations is\nconversational.\nThe interface to machines is becoming plain text, allowing teams to obtain effects\nand scale operational excellence across entire business units simply by asking\nthe right questions.",[31,2632,2634],{"id":2633},"the-context-challenge","The Context Challenge",[15,2636,2637,2638,2642,2643,2647,2648,2651],{},"Data capture involves integrating various machine protocols (like\n",[63,2639,2641],{"href":2640},"\u002Fnode-red\u002Fprotocol\u002Fopc-ua\u002F","OPC-UA"," or ",[63,2644,2646],{"href":2645},"\u002Fnode-red\u002Fprotocol\u002Fmodbus\u002F","Modbus","),\ntransporting, combining, and visualizing the information. While low-code tools\nlike Node-RED have decreased the implementation time to mere hours, the full\nproblem isn't solved: what happens ",[170,2649,2650],{},"after"," the data is collected?",[15,2653,2654,2655,2658],{},"Often, raw sensor data, like pressure, voltage, and temperature readings, lacks\ncontext to immediately understand there's a problem worth solving. Even when a dashboard\nhas been built, spotting an issue (such as a high energy consumption on ",[924,2656,2657],{},"machine 1",")\ndoesn't inherently guide the operator on how to resolve it.\nFurthermore, data flow often involves a psychological hurdle, moving from areas\nwhere an engineer feels comfortable (perhaps the data storage side) to areas of\nless expertise (like machine protocols or physical voltage readings).",[15,2660,2661],{},"The goal is to asking high-level questions, such as:\n\"What changed in the energy consumption for machine 4?\".",[31,2663,2665],{"id":2664},"text-the-new-language-of-control","Text: The New Language of Control",[15,2667,2668],{},"Removing code, often a complex layer, can be now achieved because of Large Language\nModels (LLMs) and the Model Context Protocol (MCP).",[15,2670,2671,2672,2675],{},"LLMs, like ChatGPT, predict the next word in a sentence, allowing humans to\nquery systems using ",[19,2673,2674],{},"natural language"," rather than complex code.\nLLMs face fundamental limitations though: they are typically cloud-based, can be\nslow, and are trained at specific points in time, meaning they cannot inherently\nreact to real-time, event-based data or proprietary local context.",[15,2677,2678,2679,2682],{},"This is where the ",[19,2680,2681],{},"Model Context Protocol (MCP)"," steps in. The promise of\nMCP is to give models more context through an agreed-upon protocol.\nMCP allows operators to define exactly what read-only information (resources) or\nfunctionality (tools) they want to expose to the LLM.",[39,2684,2685,2691],{},[42,2686,2687,2690],{},[19,2688,2689],{},"Resources"," are read-only, like sensor readings, employee staff lists, vacation calendars, or specification sheets (e.g., upper and lower temperature limits).",[42,2692,2693,2696],{},[19,2694,2695],{},"Tools"," are functions that allow the LLM to perform an action or change a state in the physical world.",[15,2698,2699,2700,2704],{},"By feeding this context into an MCP server (such as the official ",[63,2701,2703],{"href":2702},"\u002Fnode-red\u002Fflowfuse\u002Fmcp\u002F","FlowFuse MCP node","),\nthe LLM transforms into a powerful operational partner.",[15,2706,2707,2708,2711],{},"For example, an operator can ask: \"Can you show me the last five temp sensor readings recorded?\".\nOnce the model identifies an anomaly, the operator can incorporate specifications and ask: \"Are any of these values outside of spec for upper temp or lower temp?\".\nIf a problem is confirmed, the system can use staff and location data to answer a pointed question like: ",[19,2709,2710],{},"\"Who are all the staff located nearest to the problem, and what is the quickest way to get there?\"",".\nThis capability quickly transforms complex data into actionable steps, finding the problem, comparing it to specs, finding the right person, and routing them to the site, all within a matter of minutes.",[31,2713,2715],{"id":2714},"orchestrating-effects-across-the-machine-fleet","Orchestrating Effects Across the Machine Fleet",[15,2717,2718],{},"The ability to propagate these text-driven decisions across many machines by the\nsame team is enabled by Node-RED serving as the essential integration layer.",[15,2720,2721],{},"FlowFuse, a major corporate sponsor of Node-RED, aims to fuse the digital realm\nwith machines and the shop floor for IoT use cases. Node-RED acts as the shell\nthat connects proprietary and legacy machine protocols (OT side) to the modern MCP structure.",[15,2723,2724],{},"If a manufacturing facility wants to allow an LLM to control a physical device,\nNode-RED can integrate the machine (e.g., a Siemens S7 stack light) and wrap the\ncontrol logic in an MCP tool. The LLM requests an action\n(e.g., \"turn the stack light green\"), the MCP tool sends the action through\nNode-RED's established adapters, and the action is executed.",[15,2726,2727],{},"This means that existing organizational logic and machine adapters, which have\nalready been integrated into Node-RED, can be instantly made LLM and AI ready.\nThis rapid adaptation allows a very broad spectrum of engineers to be applied to\nproblems, moving past relying on \"tribal knowledge\" held by a single expert.",[31,2729,2731],{"id":2730},"text-driven-playbooks-and-the-human-in-the-loop","Text-Driven Playbooks and the Human in the Loop",[15,2733,2734,2735,2738],{},"Looking ahead, this technology enables the creation of ",[19,2736,2737],{},"text-driven playbooks",".\nAn operator might input a natural language prompt:\n\"How do we optimize a certain procedure in the factory?\". The resulting\noperational procedure, driven by the LLM and executed via MCP tools, becomes a\ndocumented playbook. This system helps organizations achieve operational excellence\nby turning human text input into processes that the rest of the company can read,\nunderstand, and replicate.",[15,2740,2741,2742,29],{},"However, the industry must move cautiously. A critical element of the future of\noperations is the necessity of a ",[924,2743,2744],{},"human in the loop",[15,2746,2747],{},"AI is predictive and, in certain ways, random, meaning that if context slightly\nchanges, the output is not deterministic. When the consequences of an action are\nphysical or high-stakes\n(e.g., stopping a production line, or an irreversible action like boiling an egg),\nfull control should not be handed over to a non-deterministic system. The risk of\nan AI being \"as confident when they're wrong as when they're right\" necessitates human oversight.",[15,2749,2750],{},"For the near future (the next five years), the AI acts as a partner or a fault\npartner, making the uncomfortable aspects of complex flows more manageable. It is\ncurrently best applied in reversible or digital tasks, such as generating reports\nor triggering low-consequence actions like turning a stack light orange to signal\nan engineer. As trust grows and impacts iterate, AI will gain more influence and\ncontext, but the final, consequential decisions will remain with the human.",[15,2752,2753],{},"The combination of LLMs, MCP, and Node-RED provides operators with super powers.\nThe operational floor is transforming from a place where experts write complex\ncode for singular machines, to a conversational environment where high-level, natural\nlanguage queries drive intelligent, scalable actions across the entire enterprise.",[15,2755,2756,2757,2762],{},"Ready to experience text-driven operations in your own facility?\n[Try FlowFuse for free]({% include \"sign-up-url.njk\" %}) or request a personalized demo to see how LLM-powered automation can transform your industrial processes.\n",[63,2758,2761],{"href":2759,"rel":2760},"https:\u002F\u002Fflowfuse.com\u002Fcontact-us\u002F",[67],"Contact us today"," or sign up for our upcoming webinar to stay ahead in the Industry 4.0 revolution.",{"title":103,"searchDepth":104,"depth":104,"links":2764},[2765,2766,2767,2768],{"id":2633,"depth":107,"text":2634},{"id":2664,"depth":107,"text":2665},{"id":2714,"depth":107,"text":2715},{"id":2730,"depth":107,"text":2731},"2025-11-12","Discover how FlowFuse combines LLMs and Model Context Protocol (MCP) with Node-RED to enable text-driven operations, transforming industrial data into actionable insights through natural language queries.","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Fflowfuse+llm+mcp-equals-text-driven-operations.png",{"keywords":2773,"excerpt":2774},"MCP, Node-RED MCP, LLM",{"type":12,"value":2775},[2776],[15,2777,2627],{},"\u002Fblog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations",{"title":2620,"description":2770},{"loc":2778},"blog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations","Moving from a Code driven interface to LLMs interpreting humans.",[10,138,2784],"ai","vaesYCyEESY","Svo_s3rUH5XV8xLsr6m6fXpqSh5v2pwBKLl1geXGnaw",{"id":2788,"title":2789,"authors":2790,"body":2792,"cta":3,"date":3008,"description":3009,"extension":116,"image":3010,"lastUpdated":3,"meta":3011,"navigation":129,"path":3016,"seo":3017,"sitemap":3018,"stem":3019,"subtitle":3020,"tags":3021,"tldr":3,"video":3,"__hash__":3025},"blog\u002Fblog\u002F2025\u002F11\u002Fptc-kepware-thingworx-divestment.md","The Industrial IoT Market Shift: What the PTC Divestment Means for Your Data Strategy",[2791],"pablo-filomeno",{"type":12,"value":2793,"toc":2995},[2794,2797,2800,2804,2807,2810,2813,2816,2820,2823,2826,2830,2833,2836,2839,2842,2862,2869,2873,2876,2879,2883,2886,2889,2893,2896,2910,2913,2920,2924,2927,2930,2934,2937,2940,2954,2957,2961,2964,2967,2971,2978,2992],[15,2795,2796],{},"The news of PTC selling Kepware and ThingWorx to TPG for $600 million has caused a significant impact across the industrial automation sector. For anyone working in manufacturing, OT data integration, or industrial IoT, this divestment raises important questions about the future direction of these widely-used platforms, and what it means for organizations relying on them.",[15,2798,2799],{},"But here's the thing: this isn't an isolated event. Back in July 2024, there were rumors that Autodesk might acquire PTC for $20+ billion. Those talks ultimately fell through when Autodesk walked away from the deal. Now, just months later, PTC has decided to divest two of its major industrial IoT assets. The pattern is clear: PTC is refocusing its strategy.",[31,2801,2803],{"id":2802},"a-strategic-realignment-not-a-surprise","A Strategic Realignment, Not a Surprise",[15,2805,2806],{},"PTC has been under strategic pressure for some time. The company operates across multiple domains: CAD and PLM (with Creo and Windchill), Application Lifecycle Management (ALM) with Codebeamer, and Industrial IoT with Kepware and ThingWorx. While this portfolio diversity has value, it also creates complexity, and when Autodesk considered acquiring PTC earlier this year, that complexity likely played a role in their decision to walk away.",[15,2808,2809],{},"Now, PTC is taking action on its own. By divesting Kepware and ThingWorx to TPG, the company is narrowing its focus to what it calls its \"Intelligent Product Lifecycle\" vision, concentrating on CAD, PLM, ALM, SLM (Service Lifecycle Management), and the integration of AI and SaaS across these core areas.",[15,2811,2812],{},"For PTC, this makes sense. Kepware and ThingWorx solve different problems than the rest of their portfolio. They're about OT data connectivity and IIoT application development (areas that don't naturally integrate with product design and lifecycle management). By selling these assets to TPG, PTC can focus resources on its core business while TPG invests in growing the connectivity and IoT platforms.",[15,2814,2815],{},"But for customers, this introduces uncertainty: What does life under TPG ownership look like?",[31,2817,2819],{"id":2818},"the-customer-reality-uncertainty-and-opportunity","The Customer Reality: Uncertainty and Opportunity",[15,2821,2822],{},"For organizations using Kepware or ThingWorx, this transition will bring uncertainty. Product roadmaps may shift. Pricing structures could change. Integration strategies may no longer make sense in the same way. Even if TPG commits to investing in these platforms, the reality is that customers are now operating in a period of transition (and transitions often, if not always, bring risk).",[15,2824,2825],{},"This is precisely why, back in July when the Autodesk acquisition rumors started, we launched our migration landing page at flowfuse.com\u002Fvs\u002Fkepware. To leave things clear. To make a clear statement. We saw what was coming. Organizations that had built their OT data strategies on these platforms would soon be re-evaluating their options. And for many, this is the perfect moment to ask a more fundamental question: what should a modern industrial data integration platform actually look like?",[31,2827,2829],{"id":2828},"the-open-source-alternative-built-by-node-reds-founder","The Open-Source Alternative: Built by Node-RED's Founder",[15,2831,2832],{},"At FlowFuse, we take a fundamentally different approach to industrial data integration. We're not a closed, proprietary platform tied to a single vendor's roadmap or business priorities. Instead, we're built on Node-RED, the truly open-source, low-code programming tool created by Nick O'Leary, who is also the founder of FlowFuse.",[15,2834,2835],{},"This isn't just a technical detail but it's a strategic advantage. Here's why:",[15,2837,2838],{},"FlowFuse vs. Kepware: A Clear Comparison",[15,2840,2841],{},"For organizations evaluating their options, the differences between FlowFuse and traditional platforms like Kepware become clear when you compare them side by side:",[39,2843,2844,2847,2850,2853,2856,2859],{},[42,2845,2846],{},"Open-source foundation: FlowFuse is built on Node-RED and the open-source ecosystem, giving you full transparency and control. Kepware, by contrast, is a proprietary stack with limited visibility into how it works.",[42,2848,2849],{},"Zero vendor lock-in: With FlowFuse, your flows are portable. You can run them anywhere (cloud, on-premises, or at the edge). Kepware ties you to their licensing model and roadmap.",[42,2851,2852],{},"Integration flexibility: Node-RED's 5,000+ community-contributed nodes mean you can connect to virtually anything (MQTT, OPC-UA, REST APIs, SQL databases, cloud services, and more). Kepware's integrations are often limited to proprietary or custom connectors.",[42,2854,2855],{},"Transparent pricing: FlowFuse offers use-based, transparent pricing that scales with your needs. Kepware's licensing model is tiered, often creating cost barriers as you grow.",[42,2857,2858],{},"Active community: Node-RED has one of the largest and most active open-source communities in industrial automation. You're not dependent on a single vendor for support, innovation, or problem-solving. Kepware operates in a closed ecosystem.",[42,2860,2861],{},"Future-proof architecture: FlowFuse is cloud-ready, portable, and designed to evolve with your infrastructure. After multiple ownership transitions, Kepware's future direction under TPG remains uncertain.",[15,2863,2864,2865,29],{},"You can explore these differences in detail at ",[63,2866,2867],{"href":2867,"rel":2868},"https:\u002F\u002Fflowfuse.com\u002Fvs\u002Fkepware",[67],[31,2870,2872],{"id":2871},"connect-to-anything-collect-everything","Connect to Anything, Collect Everything",[15,2874,2875],{},"Node-RED has over 5,000 community-contributed nodes, covering virtually every industrial protocol, database, cloud service, and API you can imagine. Whether you need to pull data from a Siemens S7 PLC, a Modbus device, an OPC UA server, push it to AWS IoT Core, Azure, or your own database, transform it in real-time, and visualize it on a custom dashboard. Node-RED can do it all.",[15,2877,2878],{},"And because it's open source, if a connector doesn't exist, you or the community can build it. This level of connectivity is unmatched. Kepware is powerful for protocol translation, sure, but it's primarily focused on moving data from OT systems to IT systems. Node-RED, by contrast, is a full data orchestration platform. You're not just collecting data, you're transforming, routing, analyzing, and acting on it, all in one place.",[399,2880,2882],{"id":2881},"no-vendor-lock-in-ever","No Vendor Lock-In, Ever",[15,2884,2885],{},"With proprietary platforms, your data integration strategy is tied to the vendor's business decisions. Pricing changes? You adapt. Features deprecated? You work around it. Company gets acquired? You hope for the best.",[15,2887,2888],{},"With Node-RED and FlowFuse, you're in control. The core technology is open source, maintained by a thriving global community. FlowFuse adds the enterprise-grade management, security, and scalability that industrial organizations need, but you're never locked in. If you ever decide FlowFuse isn't the right fit, your Node-RED flows are yours to take and run elsewhere. That's the power of true open source.",[399,2890,2892],{"id":2891},"enterprise-ready-from-day-one","Enterprise-Ready from Day One",[15,2894,2895],{},"One of the challenges with open-source tools is that they often require significant effort to operationalize at scale. That's where FlowFuse comes in. We provide:",[39,2897,2898,2901,2904,2907],{},[42,2899,2900],{},"Centralized management of Node-RED instances across multiple sites and edge devices",[42,2902,2903],{},"DevOps-ready workflows with version control, CI\u002FCD integration, and team collaboration features",[42,2905,2906],{},"Enterprise security with role-based access control, audit logs, and compliance-ready deployment options",[42,2908,2909],{},"Scalability to manage thousands of edge devices or cloud-based instances from a single platform",[15,2911,2912],{},"In short, FlowFuse gives you the flexibility and openness of Node-RED with the operational rigor that enterprise IT and OT teams require.",[15,2914,2915,2916],{},"To learn more take a quick look at this video and learn about these 5 reasons you should migrate to FlowFuse. ",[63,2917,2918],{"href":2918,"rel":2919},"https:\u002F\u002Fyoutu.be\u002FfUqfA521Iqg",[67],[31,2921,2923],{"id":2922},"simple-visual-and-incredibly-powerful","Simple, Visual, and Incredibly Powerful",[15,2925,2926],{},"Node-RED's low-code, visual programming interface makes it accessible to engineers who aren't developers, while still being powerful enough for complex industrial applications. You wire together nodes to create data flows, connecting sources, transforming data, triggering actions, and building dashboards. It's intuitive, fast to prototype, and easy to maintain.",[15,2928,2929],{},"This means faster time-to-value. Instead of waiting months for vendor implementations or custom development, your team can build and deploy solutions in days or weeks. And because everything is visual, it's easier to document, troubleshoot, and hand off to other team members.",[31,2931,2933],{"id":2932},"what-this-means-for-you","What This Means for You",[15,2935,2936],{},"If you're currently using Kepware or ThingWorx, this transaction isn't necessarily bad news. TPG may very well invest in these platforms and accelerate their development. But it's also a reminder of a fundamental truth in enterprise software: when you build on proprietary platforms, you are always subject to the strategic priorities of the company that owns them.",[15,2938,2939],{},"This is a good time to ask some strategic questions:",[39,2941,2942,2945,2948,2951],{},[42,2943,2944],{},"Is your data integration platform aligned with your long-term strategy?Or are you constantly adapting to vendor-driven changes?",[42,2946,2947],{},"Can you extend and customize your platform to meet your unique needs? Or are you constrained by what the vendor offers?",[42,2949,2950],{},"Do you have flexibility in how and where you deploy? Cloud, on-premises, edge, hybrid, can your platform handle all of these?",[42,2952,2953],{},"What happens if your vendor’s priorities shift again? Are you prepared for another transition?",[15,2955,2956],{},"For organizations re-evaluating their options, open-source platforms like Node-RED and FlowFuse offer a compelling alternative. You get the connectivity, flexibility, and control to build exactly the data strategy you need, without being tied to a single vendor’s roadmap or business model.",[31,2958,2960],{"id":2959},"the-path-forward","The Path Forward",[15,2962,2963],{},"The industrial automation market is evolving rapidly. As companies like PTC realign their portfolios, the organizations that thrive will be the ones that build on flexible, future-proof foundations.",[15,2965,2966],{},"At FlowFuse, we believe that foundation is open source. It's why we've built our platform on Node-RED, created by our founder Nick O'Leary. And it's why we're committed to giving industrial organizations the tools they need to take control of their data strategies, with the flexibility to adapt, the power to innovate, and the freedom to choose their own path.",[31,2968,2970],{"id":2969},"ready-to-explore-your-options","Ready to Explore Your Options?",[15,2972,2973,2974,29],{},"If you're evaluating your options, whether you're currently on Kepware, ThingWorx, or another platform, ",[63,2975,2977],{"href":2976},"\u002Fbook-demo\u002F","we'd love to talk",[15,2979,2980,2981,2986,2987,2991],{},"See ",[63,2982,2985],{"href":2983,"rel":2984},"https:\u002F\u002Fflowfuse.com\u002Fvs\u002Fkepware\u002F",[67],"how we stack up against Kepware"," to learn more about how FlowFuse compares to traditional industrial connectivity platforms, or ",[63,2988,2990],{"href":2989},"\u002Fcontact-us\u002F","contact our team"," to speak with one of our Migration Experts. We can help you understand your options, plan a migration strategy, and show you how open-source industrial data integration can transform your operations.",[15,2993,2994],{},"The industrial IoT market is shifting. Make sure your data strategy is built to adapt.",{"title":103,"searchDepth":104,"depth":104,"links":2996},[2997,2998,2999,3000,3004,3005,3006,3007],{"id":2802,"depth":107,"text":2803},{"id":2818,"depth":107,"text":2819},{"id":2828,"depth":107,"text":2829},{"id":2871,"depth":107,"text":2872,"children":3001},[3002,3003],{"id":2881,"depth":294,"text":2882},{"id":2891,"depth":294,"text":2892},{"id":2922,"depth":107,"text":2923},{"id":2932,"depth":107,"text":2933},{"id":2959,"depth":107,"text":2960},{"id":2969,"depth":107,"text":2970},"2025-11-06","PTC's $600M divestment of Kepware and ThingWorx to TPG raises critical questions for industrial organizations. Learn what this means for your data strategy and why open-source alternatives like FlowFuse offer a path forward.","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Fthe-industrial-iot-market-shift.png",{"keywords":3,"excerpt":3012},{"type":12,"value":3013},[3014],[15,3015,2796],{},"\u002Fblog\u002F2025\u002F11\u002Fptc-kepware-thingworx-divestment",{"title":2789,"description":3009},{"loc":3016},"blog\u002F2025\u002F11\u002Fptc-kepware-thingworx-divestment","Understanding the bigger picture behind the $600M Kepware and ThingWorx sale",[10,138,3022,3023,3024],"kepware","thingworx","industrial-iot","PVJ6mHLPTMCAGGrh2F-9Kb4p1U0sVZ_6BBPI04Bkb8Q",{"id":3027,"title":3028,"authors":3029,"body":3031,"cta":3,"date":3111,"description":3112,"extension":116,"image":3113,"lastUpdated":3,"meta":3114,"navigation":129,"path":3119,"seo":3120,"sitemap":3121,"stem":3122,"subtitle":3112,"tags":3123,"tldr":3,"video":3,"__hash__":3125},"blog\u002Fblog\u002F2025\u002F10\u002Fai-on-flowfuse.md","MCP and Custom AI Models on FlowFuse!",[3030],"greg-stoutenburg",{"type":12,"value":3032,"toc":3104},[3033,3036,3040,3043,3046,3049,3053,3056,3059,3062,3065,3069,3072,3081,3085,3088,3091,3094,3098,3101],[15,3034,3035],{},"We have a VERY exciting announcement today: you can now build an MCP server and upload custom-trained AI models to FlowFuse!",[31,3037,3039],{"id":3038},"ai-on-flowfuse","AI on FlowFuse",[15,3041,3042],{},"Node-RED is already the most capable and flexible low-code development environment out there. FlowFuse makes it secure, robust, and scalable.",[15,3044,3045],{},"As of today, you can now use FlowFuse to create your own MCP server and use custom-trained AI models that you've built for any application.",[15,3047,3048],{},"Build an MCP server to create an AI agent that will do whatever you've design it to do. And add a training model that you've built using your own training data, so data processing works exactly the way you want it to.",[31,3050,3052],{"id":3051},"model-context-protocol","Model Context Protocol",[15,3054,3055],{},"LLMs were trained by scraping data from the internet to build data models that can complete a task (like answering a question or writing a blog article -- but not this one!) given some input prompt. When you rely on an LLM to answer questions that are very general or perform operations that are rather straightforward, LLMs can generally do so with ease.",[15,3057,3058],{},"However, if you want to do something more sophisticated, or create an AI agent that will rely on specific data, that data needs to be presented to the AI somehow. MCP is what enables that.",[15,3060,3061],{},"Instead of relying just on LLMs trained from scraping the entire internet, the new MCP nodes enable you to create your own MCP servers, so you can present information to an AI tool, putting much more power and control in your hands as a developer.",[15,3063,3064],{},"You are now able to create your own, custom AI agent using FlowFuse.",[31,3066,3068],{"id":3067},"custom-data-models","Custom Data Models",[15,3070,3071],{},"When creating an AI agent or getting AI assistance with some task, one component is the data that is surfaced to the model. That part is handled by MCP. Another part is how the model interacts with that data. That part is handled with our new AI nodes.",[15,3073,3074,3075,3080],{},"Instead of relying on a standard LLM, even one that has been set up to connect with an MCP server, it is possible to train the model itself. The new AI nodes allow you to train a custom model, put it in ",[63,3076,3079],{"href":3077,"rel":3078},"https:\u002F\u002Fonnx.ai",[67],"ONNX"," format, and connect it to Node-RED, where it can be deployed to run any operation you wish with your new, personally-trained AI.",[31,3082,3084],{"id":3083},"the-sky-is-the-limit","The Sky Is the Limit",[15,3086,3087],{},"The flexibility of Node-RED, the reliability of FlowFuse, and the customizability enabled by these MCP and AI nodes means you can build just about any AI application you wish!",[15,3089,3090],{},"And this week, we're going to tell you all about it. Stay tuned for a demo or three, some tutorials, and overall plenty of instructional content goodness that will demonstrate the power that is now available with these AI releases!",[15,3092,3093],{},"While we're at it, we are also going to unveil a new feature right on our home page: a trained FlowFuse Expert (AI) that will teach you how to build applications in FlowFuse and Node-RED!",[31,3095,3097],{"id":3096},"try-it-now","Try it Now",[15,3099,3100],{},"Ready to try out these new nodes? If you're new to FlowFuse, [create a trial team]({% include \"sign-up-url.njk\" %}). Then, head to Manage Palette (in the hamburger menu on the right side of the Node-RED editor), click Install, and in the dropdown menu, choose FlowFuse Nodes to see the catalog of nodes that are exclusive to FlowFuse customers. From here, you can install both the MCP nodes and AI ONNX nodes.",[15,3102,3103],{},"If you're an existing FlowFuse user, begin by restarting the Node-RED instance where you want to use the nodes, then follow the same instructions as above.",{"title":103,"searchDepth":104,"depth":104,"links":3105},[3106,3107,3108,3109,3110],{"id":3038,"depth":107,"text":3039},{"id":3051,"depth":107,"text":3052},{"id":3067,"depth":107,"text":3068},{"id":3083,"depth":107,"text":3084},{"id":3096,"depth":107,"text":3097},"2025-10-13","Create your own AI agents and deploy trained models in Node-RED","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fai-on-flowfuse.png",{"keywords":3,"excerpt":3115},{"type":12,"value":3116},[3117],[15,3118,3035],{},"\u002Fblog\u002F2025\u002F10\u002Fai-on-flowfuse",{"title":3028,"description":3112},{"loc":3119},"blog\u002F2025\u002F10\u002Fai-on-flowfuse",[10,138,3124,2784],"post","IKcKTFvRCZsyI7kF7G2ox4IYygiyIOnCWwPb791AmQ4",{"id":3127,"title":3128,"authors":3129,"body":3130,"cta":3,"date":3710,"description":3711,"extension":116,"image":3712,"lastUpdated":1458,"meta":3713,"navigation":129,"path":3719,"seo":3720,"sitemap":3721,"stem":3722,"subtitle":3723,"tags":3724,"tldr":3,"video":3,"__hash__":3726},"blog\u002Fblog\u002F2025\u002F10\u002Fusing-ethernet-ip-with-flowfuse.md","EtherNet\u002FIP Integration with FlowFuse: Communicating with Allen-Bradley PLCs",[144],{"type":12,"value":3131,"toc":3691},[3132,3135,3138,3140,3143,3148,3162,3167,3181,3195,3200,3211,3215,3218,3228,3232,3235,3238,3244,3250,3254,3257,3260,3263,3267,3270,3293,3304,3308,3311,3379,3387,3394,3397,3401,3404,3431,3439,3442,3450,3456,3458,3467,3471,3474,3482,3485,3489,3492,3509,3517,3533,3536,3540,3543,3546,3561,3569,3584,3587,3592,3596,3599,3602,3628,3636,3644,3656,3659,3663,3666,3674,3678,3685],[15,3133,3134],{},"EtherNet\u002FIP is one of the most widely used industrial communication protocols for connecting PLCs, sensors, and controllers across manufacturing environments. If you're working with Allen-Bradley PLCs, whether it's ControlLogix, CompactLogix, or MicroLogix, you're using some of the most trusted automation hardware in the industry.",[15,3136,3137],{},"This guide will show you how to integrate Allen-Bradley PLCs with FlowFuse using EtherNet\u002FIP. While the focus is on Allen-Bradley, the same Node-RED nodes and techniques also work with other EtherNet\u002FIP-compatible PLCs, such as Omron and Automation Direct. You will learn both connected and unconnected messaging modes, so you can choose the approach that fits your setup. By the end of this guide, your PLCs will be communicating with FlowFuse (Node-RED with enterprise capabilities), enabling you to build powerful industrial automation workflows.",[31,3139,820],{"id":819},[15,3141,3142],{},"Before integrating your Allen-Bradley PLC with FlowFuse, ensure you have the following:",[15,3144,3145],{},[19,3146,3147],{},"PLC Configuration:",[39,3149,3150,3153,3156,3159],{},[42,3151,3152],{},"Your Allen-Bradley PLC (ControlLogix, CompactLogix, or MicroLogix) should be properly configured and operational.",[42,3154,3155],{},"Ensure that the appropriate ladder logic program is written according to your requirements and successfully downloaded to the PLC.",[42,3157,3158],{},"Verify that the controller tags or data structures you want to access are properly defined.",[42,3160,3161],{},"Note the PLC's IP address and slot number (typically slot 0 for the processor).",[15,3163,3164],{},[19,3165,3166],{},"Node-RED:",[39,3168,3169,3172],{},[42,3170,3171],{},"Install Node-RED on the device that will communicate with the Allen-Bradley PLC. You cannot install Node-RED directly on the PLC, as PLCs are controllers, not computers. You can use devices like the Raspberry Pi, Revolution Pi, or industrial PCs to connect and transfer data across systems.",[42,3173,3174,3175,3180],{},"Use the ",[63,3176,3179],{"href":3177,"rel":3178},"https:\u002F\u002Fflowfuse.com\u002Fplatform\u002Fdevice-agent\u002F",[67],"FlowFuse Device Agent"," to install Node-RED on your edge device.",[259,3182,3183],{},[15,3184,3185,3188,3189,3194],{},[19,3186,3187],{},"Why FlowFuse Device Agent?"," FlowFuse provides Node-RED with enterprise capabilities like remote management, team collaboration, device management, and DevOps pipelines, essential features for scaling industrial automation across your organization. These capabilities help streamline operations and ensure reliability in complex manufacturing environments. ",[63,3190,3193],{"href":3191,"rel":3192},"https:\u002F\u002Fapp.flowfuse.com\u002F",[67],"Sign up for free"," to get started.",[15,3196,3197],{},[19,3198,3199],{},"Network Setup:",[39,3201,3202,3205,3208],{},[42,3203,3204],{},"Verify that the device running Node-RED is on the same network as the PLC and can successfully ping the PLC's IP address.",[42,3206,3207],{},"Ensure the PLC is properly connected to the network via Ethernet with a static IP address configured.",[42,3209,3210],{},"Ensure that firewalls do not block EtherNet\u002FIP communication (typically port 44818 for both TCP and UDP).",[31,3212,3214],{"id":3213},"understanding-ethernetip-communication-modes","Understanding EtherNet\u002FIP Communication Modes",[15,3216,3217],{},"EtherNet\u002FIP is an industrial network protocol widely used to communicate with Allen-Bradley and other PLCs over Ethernet. It allows devices to exchange data efficiently and reliably, making it a common choice for industrial automation systems.",[15,3219,3220,3221,1413,3224,3227],{},"EtherNet\u002FIP supports two primary communication modes: ",[19,3222,3223],{},"connected",[19,3225,3226],{},"unconnected"," messaging. Your choice depends on how frequently you need to communicate with the PLC, the number of available connection resources, and, most importantly, what your PLC actually supports. Not all PLCs support both modes. For example, Allen-Bradley Micro800 series PLCs only support connected messaging, while ControlLogix and CompactLogix typically support both.",[399,3229,3231],{"id":3230},"connected-messaging","Connected Messaging",[15,3233,3234],{},"Connected messaging establishes a persistent session between Node-RED and the PLC using the Forward Open protocol. Once connected, data flows continuously with minimal overhead, making it ideal for real-time monitoring and high-frequency updates. The advantage is speed, after the initial handshake, communication is fast and efficient. The tradeoff is that each session consumes one of the PLC's limited connection slots, typically ranging from 8 to 32 depending on the model.",[15,3236,3237],{},"Connected messaging itself can operate in two ways:",[15,3239,3240,3243],{},[19,3241,3242],{},"Without Routing (Direct Connection):","\nUsed for PLCs that connect directly via Ethernet without backplane routing, such as the Allen-Bradley Micro800\u002F850\u002F870 series. Only the IP address is needed, no slot number or routing path is required.",[15,3245,3246,3249],{},[19,3247,3248],{},"With Routing:","\nUsed for chassis-based PLCs like ControlLogix and CompactLogix, where the processor sits in a backplane slot. In this case, the slot number must be specified, as it automatically creates a routing path through the backplane to reach the processor.",[399,3251,3253],{"id":3252},"unconnected-messaging","Unconnected Messaging",[15,3255,3256],{},"Unconnected messaging sends individual requests without maintaining a persistent connection. Each transaction is standalone, request, response, done.",[15,3258,3259],{},"This approach doesn't consume connection slots, making it ideal when resources are limited or when multiple systems need occasional PLC access. The downside is higher overhead per message, resulting in slower performance compared to connected mode.",[15,3261,3262],{},"In practice, use connected mode for frequent polling and unconnected mode for occasional reads or writes, but always check your PLC's documentation to confirm which modes are supported.",[31,3264,3266],{"id":3265},"installing-the-ethernetip-node","Installing the EtherNet\u002FIP Node",[15,3268,3269],{},"To communicate with Allen-Bradley PLCs from FlowFuse, use the popular node-red-contrib-cip-ethernet-ip node, which supports both connected and unconnected messaging modes.",[271,3271,3272,3275,3278,3281,3287,3290],{},[42,3273,3274],{},"Open your FlowFuse Instance Node-RED editor in a web browser.",[42,3276,3277],{},"Click the main menu (three horizontal lines) in the top-right corner.",[42,3279,3280],{},"Select \"Manage Palette\" from the menu.",[42,3282,3283,3284,29],{},"Switch to the \"Install\" tab and search for ",[924,3285,3286],{},"node-red-contrib-cip-ethernet-ip",[42,3288,3289],{},"Click \"Install\" next to the node name.",[42,3291,3292],{},"Wait for the installation to complete.",[15,3294,3295,3296,3299,3300,3303],{},"Once installed, you'll find the EtherNet\u002FIP nodes in your palette under the \"plc\" category. The main nodes are ",[924,3297,3298],{},"ethernet-ip in"," for reading data and ",[924,3301,3302],{},"ethernet-ip out"," for writing data to the PLC.",[31,3305,3307],{"id":3306},"configuring-the-ethernetip-node","Configuring the EtherNet\u002FIP Node",[15,3309,3310],{},"Now that you have installed the EtherNet\u002FIP node, it is time to configure the connection to your Allen-Bradley PLC.",[271,3312,3313,3316,3319,3326,3351,3362],{},[42,3314,3315],{},"Drag any EtherNet\u002FIP node onto the canvas.",[42,3317,3318],{},"Double-click the node to open its configuration panel.",[42,3320,3321,3322,3325],{},"Click the \"+\" icon next to the ",[19,3323,3324],{},"\"PLC\""," dropdown to add a new PLC configuration.",[42,3327,3328,3329],{},"Enter the basic connection details:",[39,3330,3331,3338,3344],{},[42,3332,3333,3334,3337],{},"Give your connection a descriptive name, such as ",[19,3335,3336],{},"\"Line 1 PLC\"",", for easy identification.",[42,3339,3340,3341,29],{},"Enter your PLC’s ",[19,3342,3343],{},"IP address",[42,3345,3346,3347,3350],{},"Specify the ",[19,3348,3349],{},"slot number"," where your processor is located.",[42,3352,3353,3354],{},"Configure the communication parameters:",[39,3355,3356],{},[42,3357,3358,3361],{},[19,3359,3360],{},"Cycle time"," controls how often the node communicates with the PLC (for example, 500 ms means it updates twice per second).",[42,3363,3364,3365],{},"Select the communication mode:",[39,3366,3367,3373],{},[42,3368,3369,3372],{},[19,3370,3371],{},"Standard (Unconnected)",": This is the default option supported by most PLCs.",[42,3374,3375,3378],{},[19,3376,3377],{},"Connected (With Routing)",": Use this option if you want to enable connected messaging with routing.",[15,3380,3381,3385],{},[165,3382],{"alt":3383,"dataZoomable":103,"src":3384},"EtherNet\u002FIP Node Configuration","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Feth-ip-config.png",[170,3386,3383],{},[15,3388,3389,3390,3393],{},"This node does not currently support connected messaging (without routing). I have developed node, ",[924,3391,3392],{},"@sumit_shinde_84\u002Fnode-red-contrib-cip-ethernet-ip-enhanced",", which supports connected messaging (without routing) but is still under development.",[15,3395,3396],{},"If you need connected messaging without routing, you can use that node. To enable connected messaging without routing, select Connected (no routing) from the communication mode dropdown in the configuration dialog.",[399,3398,3400],{"id":3399},"adding-tags-to-read-or-write","Adding Tags to Read or Write",[15,3402,3403],{},"After configuring the endpoint, you need to specify which tags you want to read from or write to the PLC.",[271,3405,3406,3409,3412,3428],{},[42,3407,3408],{},"In the same configuration window, switch to the \"Tags\" tab.",[42,3410,3411],{},"Click the \"+Add\" button in the top-right corner to add a new tag.",[42,3413,3414,3415],{},"Enter the tag details:",[39,3416,3417,3423],{},[42,3418,3419,3422],{},[19,3420,3421],{},"Name",": Enter the exact tag name as it appears in your PLC program (e.g., \"Motor1Speed\" or \"Program:MainProgram.Motor1Speed\")",[42,3424,3425,3427],{},[19,3426,2404],{},": Select the data type from the dropdown (e.g., BOOL, INT, DINT, REAL, etc.)",[42,3429,3430],{},"Repeat this process for each tag you want to monitor or control. You can add multiple tags that will all use the same PLC connection you configured earlier.",[15,3432,3433,3437],{},[165,3434],{"alt":3435,"dataZoomable":103,"src":3436},"Adding tags to read and write","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fadding-tags.gif",[170,3438,3435],{},[15,3440,3441],{},"If your tag belongs to a different scope, click the “+ Add” button at the top to create a new scope. Then, within that scope, add the tags in the same manner.",[271,3443,3444,3447],{"start":1868},[42,3445,3446],{},"Once you've added all your tags, click \"Add\" to save the configuration, then \"Done\".",[42,3448,3449],{},"Finally, deploy the flow by clicking the \"Deploy\" button in the top-right corner.",[15,3451,3452,3455],{},[19,3453,3454],{},"Important:"," Tag names must match exactly as they appear in your PLC program, they are case-sensitive. Make sure to select the correct data type for each tag to ensure proper communication.",[31,3457,880],{"id":879},[15,3459,3460,3461,3466],{},"Before we start, I'd like to show you what I've prepared, a flow where I'm sending commands to the PLC to control the stack light. To interact with it, I've built a nice dashboard using ",[63,3462,3465],{"href":3463,"rel":3464},"https:\u002F\u002Fdashboard.flowfuse.com\u002F",[67],"FlowFuse Dashboard",". Here's a quick demonstration:",[441,3468],{"videoid":3469,"params":464,"style":445,"title":3470},"6X9HXJLKPyo","YouTube video player",[15,3472,3473],{},"That’s the program I have downloaded to the PLC",[15,3475,3476,3480],{},[165,3477],{"alt":3478,"dataZoomable":103,"src":3479},"Program downloaded to the PLC","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fplc-program.png",[170,3481,3478],{},[15,3483,3484],{},"Now let’s learn how to build a flow that can both read and write data to your PLC.",[399,3486,3488],{"id":3487},"writing-data-to-your-plc","Writing Data to Your PLC",[15,3490,3491],{},"Writing to a PLC is where things get exciting, this is where you move from monitoring to actually controlling your equipment. Maybe you want to start a motor, adjust a setpoint, or trigger a sequence. Whatever your goal, the process is straightforward.",[271,3493,3494,3500,3503,3506],{},[42,3495,1045,3496,1216],{},[19,3497,3498],{},[924,3499,3302],{},[42,3501,3502],{},"Double-click the node to open its configuration.",[42,3504,3505],{},"Select the PLC configuration you created earlier from the dropdown.",[42,3507,3508],{},"Select the scope under which you added the tag, choose the tag that you want to write to.",[15,3510,3511,3515],{},[165,3512],{"alt":3513,"dataZoomable":103,"src":3514},"Configuring the EtherNet\u002FIP out node to write data to a PLC tag","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Feth-out-node.png",[170,3516,3513],{},[271,3518,3519,3522,3530],{"start":1868},[42,3520,3521],{},"Click \"Done\".",[42,3523,3524,3525,3529],{},"Connect an ",[19,3526,3527],{},[924,3528,1588],{}," node or any other input node to send data.",[42,3531,3532],{},"Deploy the flow and click inject button to test it.",[15,3534,3535],{},"The node expects the incoming message payload to contain the value you want to write to the tag. Ensure that the value type matches the data type you configured when adding the tag and what the PLC expects.",[399,3537,3539],{"id":3538},"reading-single-tag-from-your-plc","Reading Single Tag from Your PLC",[15,3541,3542],{},"Reading data from your PLC is the foundation of any monitoring or control system. Whether you're tracking production counts, monitoring temperatures, or checking machine status, you'll start here.",[15,3544,3545],{},"Here's how to set it up:",[271,3547,3548,3554,3556,3558],{},[42,3549,1045,3550,1216],{},[19,3551,3552],{},[924,3553,3298],{},[42,3555,3502],{},[42,3557,3505],{},[42,3559,3560],{},"Select the scope under which you added the tag, select the mode to \"Single Tag\" and choose the tag that you want to read.",[15,3562,3563,3567],{},[165,3564],{"alt":3565,"dataZoomable":103,"src":3566},"Configuring the EtherNet\u002FIP in node to read a single tag from the PLC","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fread-plc-tag.png",[170,3568,3565],{},[271,3570,3571,3573,3581],{"start":1868},[42,3572,3521],{},[42,3574,3575,3576,3580],{},"Connect a ",[19,3577,3578],{},[924,3579,1634],{}," node to view the data.",[42,3582,3583],{},"Deploy the flow.",[15,3585,3586],{},"The node will automatically read data based on your configured cycle time and output it.",[15,3588,3589],{},[170,3590,3591],{},"Note: Reading single tags individually isn't recommended for production use as it creates unnecessary network overhead. Consider using \"All tags\" mode or structuring your PLC with array data types to read multiple values efficiently in one request.",[399,3593,3595],{"id":3594},"reading-multiple-tags-from-your-plc","Reading Multiple Tags from Your PLC",[15,3597,3598],{},"Sometimes you need the full picture, not just one data point, but everything that matters for your process. Reading multiple tags at once gives you a complete snapshot of your system in a single message.",[15,3600,3601],{},"Let me show you how:",[271,3603,3604,3610,3612,3614],{},[42,3605,1045,3606,1216],{},[19,3607,3608],{},[924,3609,3298],{},[42,3611,3502],{},[42,3613,3505],{},[42,3615,1654,3616,3619,3620,3623,3624,3627],{},[19,3617,3618],{},"Mode"," field, select ",[19,3621,3622],{},"\"All tags\""," to read all configured tags together in a single message, or select ",[19,3625,3626],{},"\"All tags (one per msg)\""," if you want each tag value sent as a separate message.",[15,3629,3630,3634],{},[165,3631],{"alt":3632,"dataZoomable":103,"src":3633},"Reading all tags with each tag sent as a separate message","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fall-tags-one-msg-per-tag.png",[170,3635,3632],{},[15,3637,3638,3642],{},[165,3639],{"alt":3640,"dataZoomable":103,"src":3641},"Reading all tags together in a single message object","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fall-tags-single-obj.png",[170,3643,3640],{},[271,3645,3646,3648,3654],{"start":1868},[42,3647,3521],{},[42,3649,3575,3650,3580],{},[19,3651,3652],{},[924,3653,1634],{},[42,3655,3583],{},[15,3657,3658],{},"The node will automatically read all configured tags based on your cycle time, giving you full visibility into your PLC data.",[31,3660,3662],{"id":3661},"next-steps","Next Steps",[15,3664,3665],{},"Now that you've learned how to read and write data with your Allen-Bradley PLC, you can build interactive dashboards to monitor and control your industrial processes. FlowFuse Dashboard makes it easy to create professional interfaces with buttons, gauges, charts, and controls, all without writing complex code.",[15,3667,3668,3669,3673],{},"Check out our ",[63,3670,3672],{"href":3671},"\u002Fblog\u002F2024\u002F03\u002Fdashboard-getting-started\u002F","Getting Started with Dashboard guide"," to learn how to build your first dashboard.",[399,3675,3677],{"id":3676},"beyond-allen-bradley","Beyond Allen-Bradley",[15,3679,3680,3681,29],{},"FlowFuse isn't limited to EtherNet\u002FIP. Connect Siemens PLCs via S7, use OPC UA for vendor-neutral communication, integrate Modbus devices, or connect IoT sensors with MQTT. Mix protocols as needed, your factory floor probably isn't single-vendor anyway. For a full view of every supported PLC brand and protocol, see the ",[63,3682,3684],{"href":3683},"\u002Flanding\u002Fplc\u002F","FlowFuse PLC integration overview",[15,3686,3687,3690],{},[63,3688,3689],{"href":2976},"Book a demo"," to see how FlowFuse connects your entire operation.",{"title":103,"searchDepth":104,"depth":104,"links":3692},[3693,3694,3698,3699,3702,3707],{"id":819,"depth":107,"text":820},{"id":3213,"depth":107,"text":3214,"children":3695},[3696,3697],{"id":3230,"depth":294,"text":3231},{"id":3252,"depth":294,"text":3253},{"id":3265,"depth":107,"text":3266},{"id":3306,"depth":107,"text":3307,"children":3700},[3701],{"id":3399,"depth":294,"text":3400},{"id":879,"depth":107,"text":880,"children":3703},[3704,3705,3706],{"id":3487,"depth":294,"text":3488},{"id":3538,"depth":294,"text":3539},{"id":3594,"depth":294,"text":3595},{"id":3661,"depth":107,"text":3662,"children":3708},[3709],{"id":3676,"depth":294,"text":3677},"2025-10-10","Learn how to integrate Allen-Bradley PLCs with FlowFuse using EtherNet\u002FIP. This guide covers connected and unconnected messaging, reading and writing tags, and building industrial automation workflows in Node-RED.","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fallen-bradly-plc.png",{"keywords":3714,"excerpt":3715},"EtherNet\u002FIP, Allen-Bradley PLC, FlowFuse, Node-RED, industrial automation, connected messaging, PLC integration",{"type":12,"value":3716},[3717],[15,3718,3134],{},"\u002Fblog\u002F2025\u002F10\u002Fusing-ethernet-ip-with-flowfuse",{"title":3128,"description":3711},{"loc":3719},"blog\u002F2025\u002F10\u002Fusing-ethernet-ip-with-flowfuse","A guide to connected and unconnected EtherNet\u002FIP communication with FlowFuse",[10,138,3124,3725],"plc","Tls9rf54vSdWRttZ1o0U98x_fSTsda8vKcJVMfRL1Rw",{"id":3728,"title":3729,"authors":3730,"body":3732,"cta":3,"date":3817,"description":3818,"extension":116,"image":3819,"lastUpdated":3,"meta":3820,"navigation":129,"path":3825,"seo":3826,"sitemap":3827,"stem":3828,"subtitle":3818,"tags":3829,"tldr":3,"video":3830,"__hash__":3831},"blog\u002Fblog\u002F2025\u002F10\u002Fnode-red-revolution.md","The Node-RED Revolution: How Low-Code is Democratizing Industrial Automation",[3731],"nick-oleary",{"type":12,"value":3733,"toc":3810},[3734,3737,3740,3743,3746,3750,3753,3756,3760,3763,3767,3770,3773,3776,3780,3783,3786,3789,3792,3795,3798,3802],[15,3735,3736],{},"To understand the impact Node-RED has had on industrial automation, it's useful to understand where it has come from and what has enabled its success.",[15,3738,3739],{},"Almost 20 years ago, I was helping to develop an MQTT broker. One of the features of the broker was to create message transformations; taking messages from one topic, modifying their structure and republishing on another topic. It was a really useful feature, but it was only ever exposed via a programming API. Customers had to write lots of code to make use of it. It was also difficult to examine what transformations had already been created. I would often find myself getting a piece of paper and writing out the transformations to help spot any unexpected interactions between them. At the time, I dreamed of a way to better visualise what was happening - but that was far out of scope of our requirements at the time.",[15,3741,3742],{},"Fast forward a few years, and I found myself faced with a similar challenge; working on customer projects that often required pulling data from different sources and having to write the same bits of code again and again to get the job done. This time, I was more in a position to do something about it, and after a couple days of coding, I had a simple demo of a tool that could visualise MQTT topics and how messages would get routed between them. Then we added a way to pull data from a Serial port, a way to run custom code against the messages, a way to do HTTP requests - each week, a new customer project would raise a new requirement and this little side project would prove its worth again and again.",[15,3744,3745],{},"This is, of course, the Node-RED origin story. But how has this little side project become the widely adopted success it has - and why is it so valuable to Industrial users? I think there are three parts to this; its low-code visual nature, its Open Source availability and its extensibility.",[31,3747,3749],{"id":3748},"low-code-visualisation","Low-Code Visualisation",[15,3751,3752],{},"One of my original motivations for Node-RED was to save time by not having to write the same pieces of boiler-plate code each time I needed to do a particular task. Dragging a node into the workspace takes seconds and replaces minutes - or even hours - of traditional code development time.",[15,3754,3755],{},"This abstraction empowers anyone to start building applications in Node-RED. System engineers understand the problem they are trying to solve. Node-RED gives them the ability to build their solutions without having to think about the lines of code each node represents - they can focus on the task at hand.",[31,3757,3759],{"id":3758},"open-source","Open Source",[15,3761,3762],{},"The move away from proprietary solutions gives users more control and influence over the tools they use. Open Source Software brings principles of collaboration, transparency, and interoperability. It was a very deliberate choice to make Node-RED Open Source in its early days; we knew the project’s future lay in building an open community around it - with contributors from many backgrounds.",[31,3764,3766],{"id":3765},"extensibility","Extensibility",[15,3768,3769],{},"A natural consequence of the Open Source path the project took was to make it easy to extend what Node-RED could do. Enabling the community to build their own nodes greatly accelerated the adoption of the project - with the Open Source ethos underpinning that work.",[15,3771,3772],{},"Whilst we see Node-RED used in many industries, it finds a natural home in the Industrial automation space. Being able to run anywhere from edge-of-network devices to the cloud gives a lot of flexibility in the types of solutions it can be applied to. At its heart, Node-RED is all about accessing data, wherever it may be and doing something meaningful with that data.",[15,3774,3775],{},"We see it being used alongside traditional PLCs, providing an easier point of integration - pulling data from many different systems in one place, providing a dashboard visualization in situ, and making the data available to the wider organization. Having a consistent developer experience for all those tasks makes Node-RED a natural choice.",[31,3777,3779],{"id":3778},"scaling-node-red-within-your-organization","Scaling Node-RED within your organization",[15,3781,3782],{},"Through the early life of the project, we focussed on the core of Node-RED being a high-quality, reliable, low-code development tool. Through the community, we knew there were companies beginning to adopt Node-RED at scale, but some of the challenges they were facing were out of scope of what the core project could address.",[15,3784,3785],{},"The flexibility of Node-RED comes at a cost of how you scale and manage your Node-RED infrastructure. All organizations have a responsibility to keep their IT infrastructure secured and well-maintained - and that has to be considered when adopting any technology.",[15,3787,3788],{},"For all of its strengths, the choice of Open Source at the core does pose some questions that should not be overlooked. For example, who can be relied on to fix issues if you don’t have the engineering capacity within your own organization? How do you manage all of those Node-RED instances throughout your organization?",[15,3790,3791],{},"These were the questions that motivated me to start FlowFuse - and is where we elevate Node-RED to be a robust enterprise-ready platform. Just as the early development of Node-RED was driven by our own requirements, FlowFuse has been built based on first-hand experience of what’s needed to scale Node-RED.",[15,3793,3794],{},"A great example is one of our early customers who is a large manufacturer in the US. They began adopting Node-RED in 2018 across their facilities. At the time, they had to build a lot of custom automation to manage it, but the overheads were becoming unmanageable. With their small IT team, they found it hard to properly track the different versions of flows deployed across their sites. They moved over to FlowFuse which has enabled them to continue scaling their operations and now have thousands of Node-RED instances being managed by the platform.",[15,3796,3797],{},"This is a common pattern we see; FlowFuse solves the operational challenges that slow down digital transformation; engineers are able to focus on solving real business problems with confidence.",[31,3799,3801],{"id":3800},"join-us-at-node-red-con","Join us at Node-RED Con",[15,3803,3804,3805,29],{},"Next month we’re sponsoring the Node-RED Con virtual event - a free day of talks from the Node-RED community all about how it’s being used in industry. There are lots of fascinating talks scheduled and I’d urge you to ",[63,3806,3809],{"href":3807,"rel":3808},"https:\u002F\u002Fevents.zoom.us\u002Fev\u002FAqhqiQ8mTK2lnAoOEH8c8TA1a_9MzVhZq_T7d1-kMHlHDt2_Qh_0~AtAGpC_uhX5LxGrRFYeO63TLtQlUXVUdpy3DY5mEZFgC79PyUeKZzgp8njVUDVZdS3SBo8HS1wGVPdosNhe2VVxCpw",[67],"register to attend",{"title":103,"searchDepth":104,"depth":104,"links":3811},[3812,3813,3814,3815,3816],{"id":3748,"depth":107,"text":3749},{"id":3758,"depth":107,"text":3759},{"id":3765,"depth":107,"text":3766},{"id":3778,"depth":107,"text":3779},{"id":3800,"depth":107,"text":3801},"2025-10-09","Looking back on where Node-RED came from to understand the impact it has had on industry","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fnode-red-revolution.png",{"keywords":3,"excerpt":3821},{"type":12,"value":3822},[3823],[15,3824,3736],{},"\u002Fblog\u002F2025\u002F10\u002Fnode-red-revolution",{"title":3729,"description":3818},{"loc":3825},"blog\u002F2025\u002F10\u002Fnode-red-revolution",[10,138],"n9HhZCh0Ndg","TtQRLHIjKysoS2DVJ11Te2FjyV2MDvrFM_dGWQ021GQ",{"id":3833,"title":3834,"authors":3835,"body":3836,"cta":3,"date":3817,"description":3926,"extension":116,"image":3927,"lastUpdated":3,"meta":3928,"navigation":129,"path":3938,"seo":3939,"sitemap":3940,"stem":3941,"subtitle":3942,"tags":3943,"tldr":3,"video":3,"__hash__":3944},"blog\u002Fblog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype.md","Beyond Cloud AI Orchestration: Why the Future is Hybrid Edge-Cloud Intelligence",[2791],{"type":12,"value":3837,"toc":3919},[3838,3847,3850,3853,3856,3860,3863,3866,3869,3873,3876,3879,3883,3886,3889,3892,3896,3899,3902,3905,3909,3912],[15,3839,3840,3841,3846],{},"Congratulations to n8n on their ",[63,3842,3845],{"href":3843,"rel":3844},"https:\u002F\u002Fblog.n8n.io\u002Fseries-c\u002F",[67],"Series C funding round","! This is a fantastic milestone and a clear signal that the market has moved beyond AI experimentation and into the serious business of production deployment. Platforms like n8n are mastering what we call centralized orchestration: creating cloud-native \"brains\" that connect digital services, APIs, and data sources to execute complex workflows. This approach excels for digital-first applications and represents a crucial evolution in workflow automation.",[15,3848,3849],{},"But for industrial applications, we need to think beyond traditional cloud orchestration.",[15,3851,3852],{},"The tech world is buzzing with talk of AI agents and orchestration platforms, and the energy around n8n's recent funding is proof of this momentum. This energy is a fantastic sign of a maturing market, proving that we've moved beyond AI experimentation and into the serious business of production deployment.",[15,3854,3855],{},"Cloud orchestration platforms excel at what they're designed for: connecting digital services, managing API workflows, and orchestrating cloud-native applications. However, for the industries that power our physical world, manufacturing, logistics, energy, and infrastructure, we need complementary approaches that address the unique requirements of operational technology. This is where hybrid edge-cloud architectures become essential.",[31,3857,3859],{"id":3858},"industrial-requirements-where-cloud-only-solutions-face-challenges","Industrial Requirements: Where Cloud-Only Solutions Face Challenges",[15,3861,3862],{},"When operations involve real-world assets like factory machinery, remote sensors, or logistics fleets, purely centralized approaches encounter specific industrial constraints. For applications where milliseconds matter, such as emergency shutdowns or quality control decisions, the latency of cloud round-trips can be too slow for critical responses. Connectivity challenges in industrial environments, from remote oil platforms to underground mining operations, require systems that can operate intelligently even when cloud connections are intermittent.",[15,3864,3865],{},"Additionally, the economics of data movement become significant at industrial scale. Streaming continuous data from thousands of sensors to the cloud for processing can be costly and inefficient, especially when much of that processing could happen locally. Many industries also have regulatory requirements that mandate certain operational data remain on-premise for compliance and security reasons.",[15,3867,3868],{},"These constraints don't invalidate cloud orchestration, they highlight the need for hybrid approaches that leverage both cloud capabilities and edge intelligence where each excels.",[31,3870,3872],{"id":3871},"the-next-paradigm-hybrid-edge-cloud-intelligence","The Next Paradigm: Hybrid Edge-Cloud Intelligence",[15,3874,3875],{},"The future of industrial AI isn't choosing between cloud or edge, it's intelligently combining both. Rather than replacing cloud orchestration, the industrial world needs a hybrid architecture: a distributed nervous system where cloud orchestration handles high-level coordination and data aggregation, while edge intelligence manages real-time operations and local decision-making.",[15,3877,3878],{},"In this paradigm, AI operates at multiple levels. Edge devices handle immediate responses, predictive maintenance alerts, quality control decisions, and safety shutdowns, without waiting for cloud communication. Meanwhile, cloud orchestration excels at what it does best: aggregating data from multiple sites, running complex analytics, coordinating across systems, and managing enterprise-wide workflows. This creates a complementary relationship where each layer operates within its strengths.",[31,3880,3882],{"id":3881},"flowfuse-bridging-edge-and-cloud-intelligence","FlowFuse: Bridging Edge and Cloud Intelligence",[15,3884,3885],{},"While the market builds excellent tools for cloud-based AI orchestration, FlowFuse specializes in the hybrid approach that industrial applications demand. Built on the proven foundation of Node-RED, our platform provides engineers with a unified control tower to manage both cloud orchestration and edge intelligence at scale.",[15,3887,3888],{},"FlowFuse Cloud enables centralized management and coordination, while our edge capabilities allow thousands of Node-RED instances to operate intelligently at remote locations. This hybrid architecture lets you seamlessly integrate the physical and digital worlds, leveraging cloud orchestration for enterprise workflows while maintaining real-time edge intelligence where it matters most.",[15,3890,3891],{},"Our FlowFuse Expert simplifies the creation and management of complex logic across both cloud and edge environments, democratizing advanced automation without requiring specialized data science skills. Soon, with our upcoming AI Agent nodes, FlowFuse will enable powerful AI agents to operate seamlessly across the entire hybrid architecture, from cloud coordination to edge execution.",[31,3893,3895],{"id":3894},"conclusion-the-power-of-hybrid-intelligence","Conclusion: The Power of Hybrid Intelligence",[15,3897,3898],{},"The rise of AI orchestration platforms represents an important evolution in automation technology. These tools excel in their domain and will continue to play a crucial role in digital transformation initiatives.",[15,3900,3901],{},"For industrial applications, the future lies in hybrid architectures that combine the strengths of both approaches. Cloud orchestration provides the coordination, analytics, and enterprise integration capabilities that modern businesses require, while edge intelligence delivers the real-time responsiveness and resilience that industrial operations demand.",[15,3903,3904],{},"Choosing the right approach comes down to understanding your requirements. For digital-first applications, cloud orchestration platforms offer powerful solutions. For industrial and IoT applications that bridge physical and digital worlds, hybrid edge-cloud architectures provide the comprehensive intelligence needed to succeed.",[31,3906,3908],{"id":3907},"ready-to-build-the-future-of-industrial-ai","Ready to Build the Future of Industrial AI?",[15,3910,3911],{},"FlowFuse is built for the challenges of industrial and IoT data. Whether you're implementing Industry 4.0 initiatives or building predictive maintenance systems, we provide the platform to deploy and manage intelligence across both cloud and edge environments.",[15,3913,3914,3915],{},"[Start building today: Try FlowFuse free]({% include \"sign-up-url.njk\" %}) or ",[63,3916,3689],{"href":3917,"rel":3918},"https:\u002F\u002Fflowfuse.com\u002Fbook-demo\u002F",[67],{"title":103,"searchDepth":104,"depth":104,"links":3920},[3921,3922,3923,3924,3925],{"id":3858,"depth":107,"text":3859},{"id":3871,"depth":107,"text":3872},{"id":3881,"depth":107,"text":3882},{"id":3894,"depth":107,"text":3895},{"id":3907,"depth":107,"text":3908},"How edge-cloud hybrid AI architectures unlock new possibilities for industrial applications while leveraging the best of both worlds.","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fthe-ai-orchestration-hype.png",{"keywords":3,"excerpt":3929},{"type":12,"value":3930},[3931,3936],[15,3932,3840,3933,3846],{},[63,3934,3845],{"href":3843,"rel":3935},[67],[15,3937,3849],{},"\u002Fblog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype",{"title":3834,"description":3926},{"loc":3938},"blog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype","While cloud orchestration excels in digital workflows, the industrial world needs a hybrid approach.",[10,138,2784],"lTP9N_yKxFosx6FdXJwfkCrat_ktdgrLHTdszCT_sY4",{"id":3946,"title":3947,"authors":3948,"body":3949,"cta":3,"date":4214,"description":4215,"extension":116,"image":4216,"lastUpdated":1458,"meta":4217,"navigation":129,"path":4223,"seo":4224,"sitemap":4225,"stem":4226,"subtitle":4227,"tags":4228,"tldr":4229,"video":3,"__hash__":4230},"blog\u002Fblog\u002F2025\u002F09\u002Finstalling-node-red.md","Download Node-RED for Production: Windows, Mac, Linux, Raspberry Pi (2026)",[144],{"type":12,"value":3950,"toc":4200},[3951,3954,3958,3961,3964,3967,3970,3974,3987,3990,3993,3997,4000,4003,4006,4009,4013,4016,4019,4023,4026,4030,4033,4042,4051,4058,4067,4072,4081,4088,4092,4099,4105,4114,4117,4121,4124,4133,4136,4140,4143,4152,4160,4167,4175,4178,4186,4190,4193],[15,3952,3953],{},"Installing Node-RED is straightforward. Install Node.js, run a command, and you're ready for visual programming in industrial automation. The real challenge begins when it must run across production lines, connect to PLCs, and stay operational 24\u002F7.",[31,3955,3957],{"id":3956},"download-node-red-for-production-use","Download Node-RED for Production Use",[15,3959,3960],{},"Standalone Node-RED works great for testing, but production is different. When your flows control PLCs and critical equipment, downtime costs thousands per minute.",[15,3962,3963],{},"Hardware fails. Networks drop. Power cuts out. Your Node-RED instance must restart automatically, handle connectivity issues, and keep production running without manual intervention. Deploying, managing and scaling must be easy and quick in such environments.",[15,3965,3966],{},"Managing dozens of industrial PCs running Node-RED? You need centralized updates, secure remote access, proper backups, and role-based permissions. Your engineering team's months of flow development can't be lost to a hardware failure.",[15,3968,3969],{},"Production Node-RED needs enterprise reliability, security, and management capabilities that the basic installation simply can't provide.",[31,3971,3973],{"id":3972},"flowfuse-for-production-built-by-node-reds-creator","FlowFuse for Production: Built by Node-RED’s Creator",[15,3975,3976,3977,3982,3983,29],{},"FlowFuse was co-founded by ",[63,3978,3981],{"href":3979,"rel":3980},"https:\u002F\u002Fknolleary.net\u002Fabout\u002F",[67],"Nick O'Leary",", the creator and project lead of Node-RED. Since creating Node-RED at IBM in 2013, Nick has led its evolution from an internal IoT tool into one of the most widely used low-code platforms for industrial automation, with millions of downloads each year. You can read the history of Node-RED written by Nick ",[63,3984,3986],{"href":3985},"\u002Fblog\u002F2024\u002F02\u002Fhistory-of-nodered\u002F","here",[15,3988,3989],{},"After more than a decade of working directly with enterprise users deploying Node-RED in production environments, Nick and the FlowFuse team built FlowFuse to address the operational challenges that standalone Node-RED cannot solve at scale.",[15,3991,3992],{},"FlowFuse makes Node-RED production-ready through centralized management across industrial infrastructure, incorporating years of real-world deployment experience from the creator of Node-RED himself.",[399,3994,3996],{"id":3995},"enterprise-reliability-from-node-red-experts","Enterprise Reliability from Node-RED Experts",[15,3998,3999],{},"When hardware fails, FlowFuse keeps operations running through High Availability mode that distributes processing across multiple instances. Device Agents monitor system health and provide secure remote access for engineering teams, features designed by the same team that built and maintains Node-RED's core architecture.",[15,4001,4002],{},"Engineering teams get proper DevOps workflows that move flows through development, testing, and production with automated deployments. Device grouping makes updates reach thousands of devices possible while version control and rapid rollbacks keep projects on track.",[15,4004,4005],{},"Enterprise security comes built-in with single sign-on, role-based access control, comprehensive audit logging, and encrypted communications. Automated snapshots protect engineering work with rapid recovery when needed.",[15,4007,4008],{},"These core capabilities are just the foundation, FlowFuse includes dozens of additional features designed specifically for industrial environments, refined through direct experience deploying Node-RED across Fortune 500 manufacturers and critical infrastructure.",[399,4010,4012],{"id":4011},"trusted-by-industry-leaders","Trusted by Industry Leaders",[15,4014,4015],{},"FlowFuse is deployed in production environments across manufacturing, energy, and infrastructure sectors where Node-RED reliability is mission-critical. The platform's architecture reflects over 12 years of Node-RED development expertise and direct feedback from thousands of industrial deployments.",[15,4017,4018],{},"As the official enterprise solution developed by Node-RED's creator, FlowFuse represents the authoritative approach to production Node-RED deployments, backed by the team with the deepest expertise in the technology.",[31,4020,4022],{"id":4021},"setting-up-flowfuse","Setting Up FlowFuse",[15,4024,4025],{},"Sign up for the [30-day trial]({% include \"sign-up-url.njk\" %}) at FlowFuse, and you can get started immediately.",[399,4027,4029],{"id":4028},"step-1-add-remote-instance","Step 1: Add Remote Instance",[15,4031,4032],{},"Once the platform opens:",[271,4034,4035],{},[42,4036,4037,4038,4041],{},"Click on ",[19,4039,4040],{},"Remote Instances"," from the left sidebar",[15,4043,4044,4048],{},[165,4045],{"alt":4046,"dataZoomable":103,"src":4047},"FlowFuse Platform Dashboard","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fadd-remote-instance.png",[170,4049,4050],{},"FlowFuse platform dashboard showing Remote Instances option",[271,4052,4053],{"start":107},[42,4054,297,4055],{},[19,4056,4057],{},"Add Remote Instance",[15,4059,4060,4064],{},[165,4061],{"alt":4062,"dataZoomable":103,"src":4063},"Add Remote Instance Button","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fplatform-ff.png",[170,4065,4066],{},"Button to add a new remote Node-RED instance",[271,4068,4069],{"start":294},[42,4070,4071],{},"Give it a name and select the device type",[15,4073,4074,4078],{},[165,4075],{"alt":4076,"dataZoomable":103,"src":4077},"Device Instance Configuration","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Ftrail-add-instance.png",[170,4079,4080],{},"Configuring the remote device instance with name and type",[271,4082,4083],{"start":104},[42,4084,4085,4086],{},"Select your application and click ",[19,4087,2449],{},[399,4089,4091],{"id":4090},"step-2-install-device-agent","Step 2: Install Device Agent",[15,4093,4094,4095,4098],{},"FlowFuse shows you a device configuration window with two options. The ",[19,4096,4097],{},"One-Line Install"," handles everything automatically. It installs Node.js if missing, installs the device agent, and registers your device with the platform.",[15,4100,175,4101,4104],{},[19,4102,4103],{},"NPM Installation"," method provides manual instructions for Windows, Mac, or Linux.",[15,4106,4107,4111],{},[165,4108],{"alt":4109,"dataZoomable":103,"src":4110},"FlowFuse Device Configuration Window","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fdevice-configuration-window-2.gif",[170,4112,4113],{},"Device configuration window showing installation options for the FlowFuse Device Agent",[15,4115,4116],{},"Follow the steps given within the window, which takes less than a minute to connect your device.",[399,4118,4120],{"id":4119},"step-3-manage-your-device","Step 3: Manage Your Device",[15,4122,4123],{},"Once registered, you get complete control over your remote Node-RED instance through FlowFuse's management interface:",[15,4125,4126,4130],{},[165,4127],{"alt":4128,"dataZoomable":103,"src":4129},"FlowFuse Device Management Tools","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fff-instance-tools.gif",[170,4131,4132],{},"Tools for managing Node-RED instance remotely via FlowFuse",[15,4134,4135],{},"There are even more features available at the application level and team level that you can explore. To remove infrastructure management complexity of required services such as MQTT and PostgreSQL, they are built-in and available for use.",[399,4137,4139],{"id":4138},"step-4-start-building","Step 4: Start Building",[15,4141,4142],{},"To start building flows:",[271,4144,4145],{},[42,4146,4147,4148,4151],{},"Enable ",[19,4149,4150],{},"Developer Mode"," from the top right",[15,4153,4154,4158],{},[165,4155],{"alt":4156,"dataZoomable":103,"src":4157},"Developer Mode Option","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fdeveloper-mode.png",[170,4159,4156],{},[271,4161,4162],{"start":107},[42,4163,297,4164],{},[19,4165,4166],{},"Open Editor",[15,4168,4169,4172],{},[165,4170],{"alt":4156,"dataZoomable":103,"src":4171},"\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fopen-editor.png",[170,4173,4174],{},"Open Editor Option",[15,4176,4177],{},"Your automation flows now run with production reliability. Remote access works securely, and your team can collaborate while production continues running.",[15,4179,4180,4183],{},[165,4181],{"alt":4156,"dataZoomable":103,"src":4182},"\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fnode-red-editor.png",[170,4184,4185],{},"Node-RED Editor",[31,4187,4189],{"id":4188},"up-next","Up Next",[15,4191,4192],{},"FlowFuse bridges the gap between Node-RED's simplicity and production reliability requirements, built by the people who created Node-RED and understand its production needs better than anyone.",[15,4194,4195,4196,4199],{},"Ready to see FlowFuse in action? ",[63,4197,3689],{"href":3917,"rel":4198},[67]," and we'll show you how it works with your setup.",{"title":103,"searchDepth":104,"depth":104,"links":4201},[4202,4203,4207,4213],{"id":3956,"depth":107,"text":3957},{"id":3972,"depth":107,"text":3973,"children":4204},[4205,4206],{"id":3995,"depth":294,"text":3996},{"id":4011,"depth":294,"text":4012},{"id":4021,"depth":107,"text":4022,"children":4208},[4209,4210,4211,4212],{"id":4028,"depth":294,"text":4029},{"id":4090,"depth":294,"text":4091},{"id":4119,"depth":294,"text":4120},{"id":4138,"depth":294,"text":4139},{"id":4188,"depth":107,"text":4189},"2025-09-19","Learn how to install and run Node-RED on various platforms, such as local computer, Raspberry Pi, Mac, Linux, or Cloud. Production-ready solutions from the creators of Node-RED.","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fdownload-node-red.png",{"keywords":4218,"excerpt":4219},"download node-red, download nodered, install nodered, install node-red, getting started with node-red",{"type":12,"value":4220},[4221],[15,4222,3953],{},"\u002Fblog\u002F2025\u002F09\u002Finstalling-node-red",{"title":3947,"description":4215},{"loc":4223},"blog\u002F2025\u002F09\u002Finstalling-node-red","Scale Node-RED from prototype to production with centralized management and 24\u002F7 reliability",[10,138],"Installing Node-RED is straightforward, but running it reliably across production lines with PLCs and critical equipment requires enterprise capabilities that standalone Node-RED cannot provide. FlowFuse adds high availability, centralized device management, DevOps pipelines, secure remote access, and automated snapshots to make Node-RED production-ready at scale.","PMPfiafZjm6WklrXdEUP7opUefoicZaxo28yEC6S1co",{"id":4232,"title":4233,"authors":4234,"body":4235,"cta":3,"date":4364,"description":4365,"extension":116,"image":4366,"lastUpdated":3,"meta":4367,"navigation":129,"path":4376,"seo":4377,"sitemap":4378,"stem":4379,"subtitle":4380,"tags":4381,"tldr":3,"video":3,"__hash__":4382},"blog\u002Fblog\u002F2025\u002F08\u002Fopen-source-software-and-manufacturing.md","Winning Through Open-Source Software in Manufacturing Digitalization",[2622],{"type":12,"value":4236,"toc":4358},[4237,4246,4255,4258,4262,4271,4274,4278,4281,4284,4287,4290,4295,4298,4302,4305,4308,4311,4325,4328,4331,4334,4337,4340,4344,4347,4350],[15,4238,4239,4240,4245],{},"The manufacturing sector is in an interesting situation. It's caught between a\nfuture with immense promise and a present with high complexity. The promised ROI\nof digital transformation (DX) is into the\n",[63,4241,4244],{"href":4242,"rel":4243},"https:\u002F\u002Fwww.marketresearchfuture.com\u002Freports\u002Fdigital-transformation-in-manufacturing-market-32040",[67],"trillions of dollars","\nglobally over the next decade. For manufacturers, this is more than just an\nopportunity for incremental improvement; it is a fundamental shift to\ncompetitiveness.",[15,4247,4248,4249,4254],{},"Yet, on the other hand, a starkly different narrative is heard from the factory\nfloor. Despite the commitment of many IT and OT teams, paired with colossal\ninvestment, the path to digitalization is proving to be unexpectedly slow. About\n70% of digital transformation projects are (self-reported!)\n",[63,4250,4253],{"href":4251,"rel":4252},"https:\u002F\u002Fwww.myhubintranet.com\u002Fdigital-transformation-statistics\u002F",[67],"considered failures",".\nFalling short of their objectives and failing to deliver the promised return on\ninvestment (ROI).",[15,4256,4257],{},"To break this impasse a strategic shift and pivot will be needed, industrial IT\nand OT need a decisive pivot to open-source software (OSS). With its core\nprinciples of collaboration, transparency, and interoperability, manufacturers\ncan systematically remove barriers that have slowed their DX. Open source\nprovides the tools and the philosophy necessary to acquire and transport data\nmore effectively, build powerful analytics and interaction applications that\ndeliver real ROI, and, most of all, escape vendor lock-in.",[31,4259,4261],{"id":4260},"the-digitalization-paradox-in-manufacturing","The Digitalization Paradox in Manufacturing",[15,4263,4264,4265,4270],{},"The economic case for digital transformation in manufacturing is unequivocal and\nimmense. The global digital transformation market, valued at nearly $1 trillion\nin 2020, is forecasted to be\n",[63,4266,4269],{"href":4267,"rel":4268},"https:\u002F\u002Fwww.themanufacturer.com\u002Farticles\u002Fdigital-transformation-in-manufacturing-the-challenges\u002F",[67],"over $2.7 trillion by 2026",".\nThis growth is driven by the operational benefits; the primary motivators for\nthese investments are to achieve greater operational efficiency (cited by 40% of\nexecutives), a faster time-to-market for new products (36%), and an improved\ncustomer experience (35%).",[15,4272,4273],{},"To understand the paradox, one must dissect why manufacturing initiatives fail.\nThese challenges are connected with one-another, and are symptom from more\nsystemic issues. Financial constraints and ROI, particularly high initial costs\nfor hardware, software, and integration, coupled with pressure for rapid ROI,\ncreate a catch-22. DX is now viewed as a cost center rather than a source of\noperational improvement and thus ROI. A lack of qualified workers and in-house\nskills, especially in IT, is another roadblock. With manufacturing competing for\ntalent with tech and finance. Then there’s legacy systems, where factory floors\nhave old, incompatible machinery and systems, making upgrading or retrofitting\nthis \"rigid infrastructure\" costly and complex, often stalling or killing\nprojects. Finally, cybersecurity fears arise from increased connectivity in\nfactories, expanding the attack surface and making the implementation of robust\nand costly cybersecurity solutions complex, especially for SMEs.",[31,4275,4277],{"id":4276},"the-iron-grip-of-proprietary-systems-and-vendor-lock-in","The Iron Grip of Proprietary Systems and Vendor Lock-In",[15,4279,4280],{},"The challenges detailed in the earlier, stem from the same key issue – the\nmanufacturing industry's historical and continued reliance on a technological\nparadigm built around closed, proprietary systems. This licensing model, which\nwas key in enabling the first wave of digital automation, has now become a\nbarrier. Its defining characteristic is vendor lock-in, a condition that\nsystematically stifles innovation, inflates licensing costs, and holds the\nindustrial sector as a whole back from progress.",[15,4282,4283],{},"Innovation has always driven industrial automation forward. The transition from\nmechanical systems to electrical relay logic in the early 20th century, and then\nto the maturing of Programmable Logic Controller (PLC) in the 1960s, transformed\nthe factory floor. PLCs, followed by Supervisory Control and Data Acquisition\n(SCADA) and Distributed Control Systems (DCS), brought control, reliability, and\nprogrammability to industrial processes. These technologies were the engines of\nthe Third Industrial Revolution. Industrial automation was leading the way in\nwhat was possible with digital systems, and combining these systems with one\nanother in new ways. The manufacturers were driving innovation in the software\nindustry and gaining real momentous ROI.",[15,4285,4286],{},"Since the late seventies, software became a copywrightable good, and with a shift\nhappened mostly in traditional applications of the software – Vendor Lock-In.",[15,4288,4289],{},"Vendor lock-in is an economic and technical condition where a customer becomes\nso dependent on a specific vendor's products and services that switching to an\nalternative provider becomes prohibitively difficult or costly.",[15,4291,4292],{},[19,4293,4294],{},"With that, manufacturers used to lead the way three quarters into the 20th\ncentury, now are stuck with their 21st-century digital ambitions.",[15,4296,4297],{},"The traditional, vertically integrated model of industrial digitalization,\n‘featuring’ vendor lock-in, is a critical hindrance currently. This paradigm now\nshowcases inflated costs, lack of innovation, interoperability issues, and a\nskills gap. These issues have effectively made manufacturers followers rather\nthan leaders in the realm of software-aided business. To regain an edge a\nfundamental shift is a requirement. This shift requires abandoning vertical\npoint-solutions and instead embracing a new technological foundation that is\ninherently open, fostering interoperability, and freeing manufacturers from the\nconstraints of single-vendor control.",[31,4299,4301],{"id":4300},"dx-with-open-source-software","DX with Open-Source Software",[15,4303,4304],{},"In contrast to the closed, proprietary software, the open-source variant offers\na fresh approach to creating and distributing software. Born from the need of\ncollaboration and transparency, open-source software (OSS) provides fundamentals\nto break vendor lock-in once and for all.",[15,4306,4307],{},"In 1983, MIT programmer Richard Stallman, annoyed by the practice of withholding\nsource code, founded the Free Software Foundation. His goal was to create a\ncompletely free (“Free” as in speech, and also “Free” as in beer) operating\nsystem, establishing a philosophy built on the user's right to run, study,\nmodify, and redistribute software. In the late 1990s, this movement gained a\nmore pragmatic and business-friendly identity with the coining of the term \"open\nsource\".",[15,4309,4310],{},"The value proposition of OSS is great and shows up in four key points:",[271,4312,4313,4316,4319,4322],{},[42,4314,4315],{},"Cost-Effectiveness",[42,4317,4318],{},"High flexibility and extensibility",[42,4320,4321],{},"Cross-Industry collaboration",[42,4323,4324],{},"Stability and Strategic Control",[15,4326,4327],{},"To go over each, one by one.",[15,4329,4330],{},"The most immediate appeal of OSS is the absence of high upfront licensing fees.\nOSS is not \"free\" when considering the total cost of ownership (TCO), which\nincludes implementation, integration, training, and support. The crucial\ndifference lies in where the money goes. With proprietary software, a large\nportion of the budget is spent on license and maintenance fees that primarily\nbenefit the vendor. With OSS, that same capital can be reinvested into\nactivities that build direct value for the manufacturer.",[15,4332,4333],{},"Secondly, flexibility of software and freedom to extend it – the single most\npowerful advantage of OSS. Because the source code is accessible, manufacturers\nare no longer constrained by a vendor's limited feature set or development\nroadmap. Anyone can propose changes that modify, adapt, and extend the software\nto meet their operational needs. If a particular open-source tool doesn't\nsupport a niche industrial protocol, the company has the option to build that\nintegration itself or hire a third party to do so. As these investments do not\nprovide an edge over competition, it’s almost always shared within the OSS\ncommunity – building libraries of open toolchains.",[15,4335,4336],{},"Extending on that; Cross-industry collaboration enables wildly different\nuse-cases with the same fundamental problems to resolve to apply their knowledge\nand craft serving all other businesses. For example, database technology has\nbeen perfected for decades by banking and trading companies to allow for atomic\ntransactions and durability of data of those transactions. These scalability\nimprovements have profound impact for manufacturing use-cases around the world,\nhelping out with compliance, track-and-trace, and more.",[15,4338,4339],{},"Finally, Long-Term Stability and Strategic Control is implicitly featured with\nOSS. With proprietary software, a manufacturer is exposed to the business risks\nof the vendor. If the vendor is acquired, goes out of business, or simply\ndecides to discontinue a product line, the manufacturer can be left with an\nunsupported and obsolete system. With OSS, the code is publicly distributed and\ncannot be withdrawn. Even if the original maintainers abandon a project, the\ncommunity, or the company itself, can \"fork\" the code and continue its\ndevelopment. This provides a level of long-term stability and strategic control\nover critical technology assets that is impossible to achieve in a\nvendor-dependent relationship.",[31,4341,4343],{"id":4342},"finale","Finale",[15,4345,4346],{},"The manufacturing industry faces a challenge with its digital transformation\nefforts. A reliance on proprietary systems has contributed to an impasse where\ninvestment does not always produce the expected results. This model,\ncharacterized by vendor lock-in, can lead to higher costs and constraints on\ninnovation, causing manufacturers to lag in technology adoption.",[15,4348,4349],{},"To move forward, a different approach can be considered. Open-source software\n(OSS) offers a model that addresses many of the barriers causing digital\ntransformation projects to fail. With OSS, companies can gain more strategic\ncontrol over their technology, redirect funds from licensing fees toward\nin-house capabilities, and connect previously isolated systems. This allows a\nmanufacturer to shift from being a consumer of a vendor's technology to a\ndirector of its own.",[15,4351,4352,4353,4357],{},"The conversation is therefore shifting from if manufacturing should adopt open\nsource, to how it can be implemented to improve operations and deliver a return\non investment. This transition presents new questions, but it is one that\ncompanies do not have to approach on their own. At FlowFuse, we work with\nmanufacturers to navigate this change, and we would love to explain how an\nopen-source strategy can transform your factory of the future. Download our comprehensive whitepaper \"",[63,4354,4356],{"href":4355},"\u002Fwhitepaper\u002Fopen-source-software-for-manufacturing\u002F","Open Source Software for Manufacturing",",\" to learn how to transform your factory operations and secure a competitive edge.",{"title":103,"searchDepth":104,"depth":104,"links":4359},[4360,4361,4362,4363],{"id":4260,"depth":107,"text":4261},{"id":4276,"depth":107,"text":4277},{"id":4300,"depth":107,"text":4301},{"id":4342,"depth":107,"text":4343},"2025-08-20","Explore how open-source software can help manufacturers overcome vendor lock-in, accelerate digital transformation, and regain competitive advantage in Industry 4.0","blog\u002F2025\u002F08\u002Fimages\u002Fopen-source-software-and-manufacturing.png",{"keywords":4368,"excerpt":4369},"Open Source Software, Digital Transformation, DX",{"type":12,"value":4370},[4371],[15,4372,4239,4373,4245],{},[63,4374,4244],{"href":4242,"rel":4375},[67],"\u002Fblog\u002F2025\u002F08\u002Fopen-source-software-and-manufacturing",{"title":4233,"description":4365},{"loc":4376},"blog\u002F2025\u002F08\u002Fopen-source-software-and-manufacturing","Manufacturing organizations used to be leading in software but has lost the lead. Open Source Software is the way to now catch up",[10,138],"LDzf5NKl9GW4AUvYVrthsft99vx35UlOQ9QfDmt0rYw",{"id":4384,"title":4385,"authors":4386,"body":4387,"cta":3,"date":4492,"description":4493,"extension":116,"image":4494,"lastUpdated":3,"meta":4495,"navigation":129,"path":4506,"seo":4507,"sitemap":4508,"stem":4509,"subtitle":4510,"tags":4511,"tldr":3,"video":3,"__hash__":4514},"blog\u002Fblog\u002F2025\u002F07\u002Fcertified-nodes-v2.md","Curated Node-RED Integrations: FlowFuse Certified Nodes 2.0",[2622],{"type":12,"value":4388,"toc":4485},[4389,4395,4399,4402,4409,4412,4432,4436,4439,4465,4468,4472,4475,4478,4482],[15,4390,4391,4392,29],{},"At FlowFuse, we believe that enterprises deserve access to the best of Node-REDs\ncustom nodes and solutions crafted by recognized experts who bring deep domain\nknowledge and professional standards to their work. That's why we are thrilled\nto announce a significant evolution of our ecosystem: ",[19,4393,4394],{},"FlowFuse Certified Nodes\nv2.0",[31,4396,4398],{"id":4397},"from-community-contribution-to-professional-partnership","From Community Contribution to Professional Partnership",[15,4400,4401],{},"The Node-RED community has always had a pasionate and thrived on the\ndomain-expertise of its developers. The community has created an environment of\ninnovation, and now we're taking the next step to support enterprise-grade\nsolutions that companies are building on Node-RED today.",[15,4403,4404,4405,4408],{},"Our new ",[19,4406,4407],{},"Certified Nodes"," program we hope to elevate the community. We are\nestablishing a professional partnership with the most skilled node developers\nand domain experts in the community.",[15,4410,4411],{},"Here’s how it works:",[39,4413,4414,4420,4426],{},[42,4415,4416,4419],{},[19,4417,4418],{},"Expertise Recognition:"," FlowFuse partners with developers who have\ndemonstrated deep expertise in their domains, and may have already published\nnodes in their area of expertise. Ensuring our enterprise customers access the\nmost knowledgeable contributors in each field.",[42,4421,4422,4425],{},[19,4423,4424],{},"Quality-Driven Selection:"," Our certification process identifies nodes that\nexcel in real-world environments, focusing on reliability, security,\nup-to-date documentation and professional implementation standards.",[42,4427,4428,4431],{},[19,4429,4430],{},"Comprehensive Solutions:"," We welcome both open-source innovations and\nproprietary solutions. This approach allows experts to share their knowledge\nbroadly while also providing targeted solutions for specific industry needs.",[31,4433,4435],{"id":4434},"what-does-it-take-to-become-a-certified-publisher","What Does It Take to Become a Certified Publisher?",[15,4437,4438],{},"We are seeking partners who represent the pinnacle of expertise in their\nrespective domains. To join our certified ecosystem, we look for developers who\ndemonstrate:",[39,4440,4441,4447,4453,4459],{},[42,4442,4443,4446],{},[19,4444,4445],{},"Domain Expertise:"," Deep knowledge and proven experience in the specific\ntechnology or industry vertical their nodes address",[42,4448,4449,4452],{},[19,4450,4451],{},"Professional Standards:"," Commitment to enterprise-grade code quality,\ncomprehensive testing, and security best practices",[42,4454,4455,4458],{},[19,4456,4457],{},"Continuous Innovation:"," Ongoing development and enhancement of features\nthat solve real-world enterprise challenges",[42,4460,4461,4464],{},[19,4462,4463],{},"Expert Support:"," Ability to provide advanced technical support and guidance\nthat goes beyond basic troubleshooting",[15,4466,4467],{},"Our certification process ensures that every node in the certified nodes\necosystem meets these exacting standards. When enterprises choose certified\nnodes, they gain access not just to code, but to the expertise and ongoing\nsupport of recognized industry professionals. This creates a sustainable model\nwhere expert knowledge is properly valued and enterprises receive the\nreliability they require.",[31,4469,4471],{"id":4470},"why-partner-with-flowfuse","Why Partner with FlowFuse?",[15,4473,4474],{},"Joining the FlowFuse Certified Nodes v2.0 program means becoming part of an\nelite community of experts. Moreover, there's now a financial reward for doing\nso. FlowFuse commits a fixed percentage of our FlowFuse Enterprise Platform Fee\nto be distributed quarterly to the node authors.",[15,4476,4477],{},"If you are an expert developer or organization that has built exceptional\nNode-RED nodes and are committed to maintaining the highest professional\nstandards, we want to collaborate with you. Let's work together to create a more\nsustainable, professional ecosystem that connects enterprise users with the\nindustry's leading experts.",[399,4479,4481],{"id":4480},"contact-us-to-discuss-your-node-certification","Contact us to discuss your node certification",[15,4483,4484],{},"{% set formId = certificationHubspot.formId %}\n{% set targetId = certificationHubspot.targetId %}\n{% set cta = \"cta-certified-nodes-contact\" %}\n{% set reference = \"certified-nodes-v2\" %}\n{% include \"hubspot\u002Fhs-form.njk\" %}",{"title":103,"searchDepth":104,"depth":104,"links":4486},[4487,4488,4489],{"id":4397,"depth":107,"text":4398},{"id":4434,"depth":107,"text":4435},{"id":4470,"depth":107,"text":4471,"children":4490},[4491],{"id":4480,"depth":294,"text":4481},"2025-07-01","FlowFuse announces Certified Nodes v2.0 - connecting enterprises with the highest quality Node-RED nodes, built and maintained by recognized experts in their fields.","blog\u002F2025\u002F07\u002Fimages\u002Fcertified-nodes-v2.png",{"templateEngineOverride":4496,"certificationHubspot":4497,"excerpt":4500},"njk,md",{"formId":4498,"targetId":4499},"6e02fe34-13c3-442b-8c27-9a12e72bba37","hs-form-certified-nodes-contact",{"type":12,"value":4501},[4502],[15,4503,4391,4504,29],{},[19,4505,4394],{},"\u002Fblog\u002F2025\u002F07\u002Fcertified-nodes-v2",{"title":4385,"description":4493},{"loc":4506},"blog\u002F2025\u002F07\u002Fcertified-nodes-v2","FlowFuse Unveils Certified Nodes Program to Reward Quality and Ensure Long-Term Support",[10,138,4512,4513],"certified-nodes","developers","D0hoQeoEtfcN7s5t988gnzObTVHRwbWfQtez_BVDSLw",{"id":4516,"title":4517,"authors":4518,"body":4519,"cta":3,"date":4584,"description":4585,"extension":116,"image":4586,"lastUpdated":4587,"meta":4588,"navigation":129,"path":4597,"seo":4598,"sitemap":4599,"stem":4600,"subtitle":4601,"tags":4602,"tldr":3,"video":3,"__hash__":4603},"blog\u002Fblog\u002F2025\u002F06\u002Fannouncing-node-red-con-2025.md","Announcing Node-RED Con 2025: A Community Conference on Industrial Applications",[10],{"type":12,"value":4520,"toc":4580},[4521,4531,4538,4542,4545,4548,4568,4572],[15,4522,4523,4524,4527,4528],{},"At FlowFuse, our commitment to the Node-RED community is at the heart of everything we do. That's why we are thrilled to announce our support as a sponsor for ",[19,4525,4526],{},"Node-RED Con 2025",", a free, online conference taking place on ",[19,4529,4530],{},"Tuesday, November 4, 2025!",[15,4532,4533,4534,4537],{},"This year's event is dedicated to exploring a vital theme: ",[19,4535,4536],{},"Node-RED applications in industry",". It’s a fantastic opportunity for developers, engineers, and innovators to connect and share how Node-RED is being used to solve real-world challenges.",[31,4539,4541],{"id":4540},"the-agenda-is-underway","The Agenda is Underway",[15,4543,4544],{},"The conference agenda is built from community submissions, and the Call for Papers received a great response. The organizing team reports receiving many excellent submissions, so we're in for some really interesting sessions.",[15,4546,4547],{},"The event will offer sessions in three different categories:",[39,4549,4550,4556,4562],{},[42,4551,4552,4555],{},[19,4553,4554],{},"Full Talks & Demos (25-30 mins):"," Focused on industrial use cases, IoT architectures, and edge computing.",[42,4557,4558,4561],{},[19,4559,4560],{},"Lightning Talks (8-10 mins):"," Showcasing fun, creative, or inspiring projects.",[42,4563,4564,4567],{},[19,4565,4566],{},"Expert Panelists:"," To discuss the future of industrial automation with Node-RED.",[31,4569,4571],{"id":4570},"registration-is-now-open","Registration is Now Open",[15,4573,4574,4575,4579],{},"Head to ",[63,4576,4578],{"href":65,"rel":4577},[67],"the Node-RED Con 2025 official page"," to secure your spot. Join the live event on November 4, ask your questions, be part of the conversation, this only happens once a year, and you won't want to miss what the community has built!",{"title":103,"searchDepth":104,"depth":104,"links":4581},[4582,4583],{"id":4540,"depth":107,"text":4541},{"id":4570,"depth":107,"text":4571},"2025-06-12","FlowFuse is proud to sponsor Node-RED Con 2025, a free online conference on November 4, 2025, dedicated to industrial applications. Learn more and submit your talk.","\u002Fblog\u002F2025\u002F06\u002Fimages\u002Fnode-red-con-2025.png","2025-08-21",{"excerpt":4589},{"type":12,"value":4590},[4591],[15,4592,4523,4593,4527,4595],{},[19,4594,4526],{},[19,4596,4530],{},"\u002Fblog\u002F2025\u002F06\u002Fannouncing-node-red-con-2025",{"title":4517,"description":4585},{"loc":4597},"blog\u002F2025\u002F06\u002Fannouncing-node-red-con-2025","We're excited to support this year's community conference focused on Node-RED in industry. The Call for Papers is now open!",[10,136,137,138],"sLFE1hpgNui3x8-4DAQ3Hy-TazW7AKklJ7n7BoMK5tk",{"id":4605,"title":4606,"authors":4607,"body":4608,"cta":3,"date":5615,"description":5616,"extension":116,"image":5617,"lastUpdated":3,"meta":5618,"navigation":129,"path":5627,"seo":5628,"sitemap":5629,"stem":5630,"subtitle":5631,"tags":5632,"tldr":3,"video":3,"__hash__":5633},"blog\u002Fblog\u002F2025\u002F02\u002Fmonitoring-system-health-performance-scale-flowfuse.md","Monitoring Device Health and Performance at Scale with FlowFuse",[144],{"type":12,"value":4609,"toc":5590},[4610,4619,4622,4626,4630,4633,4636,4639,4642,4646,4649,4651,4654,4666,4673,4676,4689,4714,4718,4721,4724,4728,4731,4759,4762,4770,4774,4777,4794,4797,4805,4816,4820,4823,4837,4840,4848,4852,4855,4869,4872,4878,4889,4892,4895,4899,4902,4909,4912,4915,4958,4961,4979,4988,4991,4999,5006,5013,5021,5024,5028,5031,5039,5046,5050,5074,5078,5161,5164,5168,5221,5224,5228,5271,5274,5277,5345,5349,5352,5356,5361,5372,5377,5388,5393,5403,5408,5419,5429,5433,5441,5451,5462,5469,5473,5478,5488,5498,5503,5513,5518,5528,5533,5543,5548,5558,5563,5569,5577,5579,5581,5584,5587],[15,4611,4612,4613,4618],{},"Edge devices are everywhere, and their numbers are skyrocketing, from 2.7 billion in 2020 to a projected 7.8 billion by 2030, according to ",[63,4614,4617],{"href":4615,"rel":4616},"https:\u002F\u002Ftransformainsights.com\u002Fnews\u002Fedge-computing-rapid-growth-iot#:~:text=New%20Transforma%20Insights%20reports%20covering,edge%20capabilities%20in%20IoT%20devices.",[67],"various reports",". As these devices become critical for automation and data processing, monitoring their health is essential to ensure reliability and efficiency.",[15,4620,4621],{},"Tracking CPU usage, memory, and system performance helps detect potential issues early, preventing downtime and optimizing operations. In this post, we will explore how to monitor devices using Node-RED and scale this process efficiently with FlowFuse.",[441,4623],{"videoid":4624,"params":464,"style":4625,"title":3470},"43te5aD1RRw","width: 704px; height: 100%;",[31,4627,4629],{"id":4628},"what-is-device-health-monitoring-and-why-is-it-important","What is Device Health Monitoring, and Why is it Important?",[15,4631,4632],{},"Edge devices power IoT and automation, handling communication and data processing. As their numbers grow, ensuring they run efficiently is crucial.",[15,4634,4635],{},"Monitoring device health means tracking key metrics like CPU usage, memory, uptime, and system load. High CPU usage or low memory can slow down processes, disrupt data flow, and reduce efficiency.",[15,4637,4638],{},"For example, in manufacturing, edge devices connect machines to cloud systems for real-time data. If a device fails, production can be impacted.",[15,4640,4641],{},"Regular monitoring helps detect issues early, prevents downtime, and keeps devices running smoothly.",[31,4643,4645],{"id":4644},"getting-started-with-monitoring-devices","Getting Started with Monitoring Devices",[15,4647,4648],{},"We will begin by monitoring a single device, such as a Raspberry Pi, collecting system data, and visualizing it using FlowFuse. Once the process is clear, we will expand it to monitor multiple devices at scale.",[399,4650,820],{"id":819},[15,4652,4653],{},"Before you begin, ensure you have the following:",[271,4655,4656],{},[42,4657,4658,4661,4662,4665],{},[19,4659,4660],{},"Running Node-RED Instance:"," You need a running Node-RED instance on the device you want to monitor. The easiest way to set this up is with the ",[63,4663,3179],{"href":4664},"\u002Fplatform\u002Fdevice-agent\u002F",", which provides secure remote access, real-time collaboration, snapshots for quick recovery, DevOps tools, and device group management. With it, you can push updates to multiple devices with a single click.",[15,4667,4668,4669,29],{},"For a step-by-step installation guide, refer to the ",[63,4670,4672],{"href":4671},"\u002Fdocs\u002Fdevice-agent\u002Fquickstart\u002F","FlowFuse Device Agent Quickstart",[15,4674,4675],{},"If you haven’t yet signed up for a FlowFuse account, [sign up now]({% include \"sign-up-url.njk\" %}?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Monitoring%20Device%20Health%20and%20Performance%20at%20Scale%20with%20FlowFuse).",[271,4677,4678],{},[42,4679,4680,4683,4684,992],{},[19,4681,4682],{},"Required Node-RED Nodes:"," To collect system data and display it on a dashboard, install the following Node-RED nodes via the ",[63,4685,4688],{"href":4686,"rel":4687},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Feditor\u002Fpalette\u002Fmanager",[67],"Node-RED Palette Manager",[39,4690,4691,4697,4703,4708],{},[42,4692,4693,4696],{},[924,4694,4695],{},"node-red-contrib-os",": Retrieves system information such as memory, uptime, and load.",[42,4698,4699,4702],{},[924,4700,4701],{},"node-red-contrib-cpu",": Monitors CPU usage.",[42,4704,4705,4707],{},[924,4706,1536],{},": Provides UI components for visualizing system metrics.",[42,4709,4710,4713],{},[924,4711,4712],{},"node-red-contrib-moment",": Formats uptime duration in a human-readable format.",[399,4715,4717],{"id":4716},"collecting-cpu-and-system-metrics-with-node-red","Collecting CPU and System Metrics with Node-RED",[15,4719,4720],{},"Now that Node-RED is running on your device, it’s time to gather essential system metrics. Monitoring CPU usage, memory consumption, system uptime, and load averages helps you monitormonitor performance and spot potential issues before they become serious problems.",[15,4722,4723],{},"Let’s break it down step by step.",[1165,4725,4727],{"id":4726},"collecting-cpu-usage-data","Collecting CPU Usage Data",[15,4729,4730],{},"To start, let’s capture CPU usage in real time:",[271,4732,4733,4739,4742,4748,4753],{},[42,4734,1263,4735,4738],{},[19,4736,4737],{},"CPU"," node from the \"Performance\" category onto the canvas.",[42,4740,4741],{},"Double-click the node and uncheck all options except \"Send a message for overall usage.\" This ensures you get a clear view of total CPU performance. If you need per-core metrics, you can enable the other options.",[42,4743,4744,4745,4747],{},"Add an ",[19,4746,1215],{}," node, double-click it, and set it to trigger at a suitable interval (e.g., every second, every 10 seconds, or every 30 seconds). Connect its output to the CPU node.",[42,4749,1321,4750,4752],{},[19,4751,1324],{}," node and connect it to the output of the CPU node. This lets you view CPU data in the debug pane.",[42,4754,4755,4758],{},[19,4756,4757],{},"Click Deploy"," in the top-right corner of the Node-RED editor.",[15,4760,4761],{},"Your debug pane will now start showing live CPU usage data:",[15,4763,4764,4768],{},[165,4765],{"alt":4766,"dataZoomable":103,"src":4767},"An image showing the flow that gathers CPU usage data and prints it in the debug pane","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fcpu-usage.png",[170,4769,4766],{},[1165,4771,4773],{"id":4772},"monitoring-memory-usage","Monitoring Memory Usage",[15,4775,4776],{},"Next, let’s track memory consumption:",[271,4778,4779,4782,4785,4788,4791],{},[42,4780,4781],{},"Drag a Memory node onto the canvas and double-click it.",[42,4783,4784],{},"Choose the unit for memory display (e.g., gigabytes for easier readability).",[42,4786,4787],{},"Connect the Memory node’s input to the Inject node’s output.",[42,4789,4790],{},"Connect the Memory node’s output to the existing Debug node.",[42,4792,4793],{},"Click Deploy to start monitoring.",[15,4795,4796],{},"Once deployed, you will see a structured object in the debug pane containing along with cpu usage:",[15,4798,4799,4803],{},[165,4800],{"alt":4801,"dataZoomable":103,"src":4802},"An image showing the flow that gathers memory usage data and prints it in the debug pane","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fmemory-usage.png",[170,4804,4801],{},[39,4806,4807,4810,4813],{},[42,4808,4809],{},"totalmem: Total available memory",[42,4811,4812],{},"freemem: Free memory",[42,4814,4815],{},"memusage: Current memory usage",[1165,4817,4819],{"id":4818},"tracking-system-uptime","Tracking System Uptime",[15,4821,4822],{},"Monitoring uptime helps detect unexpected reboots and ensures system stability.",[271,4824,4825,4828,4831,4834],{},[42,4826,4827],{},"Drag an Uptime node onto the canvas.",[42,4829,4830],{},"Connect its input to the Inject node’s output.",[42,4832,4833],{},"Connect its output to the Debug node.",[42,4835,4836],{},"Click Deploy to activate uptime tracking.",[15,4838,4839],{},"Each time the Inject node triggers, the debug pane will display the uptime in seconds and CPU and memory usage.",[15,4841,4842,4846],{},[165,4843],{"alt":4844,"dataZoomable":103,"src":4845},"An image showing the flow that gathers system uptime data and prints it in the debug pane","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fuptime.png",[170,4847,4844],{},[1165,4849,4851],{"id":4850},"analyzing-load-average","Analyzing Load Average",[15,4853,4854],{},"To understand how busy your system has been over time, let’s analyze the load average:",[271,4856,4857,4862,4864,4866],{},[42,4858,1263,4859,1216],{},[19,4860,4861],{},"Loadavg",[42,4863,4830],{},[42,4865,4833],{},[42,4867,4868],{},"Click Deploy to start tracking.",[15,4870,4871],{},"This will give you three key metrics:",[15,4873,4874],{},[165,4875],{"alt":4876,"dataZoomable":103,"src":4877},"An image showing the flow that gathers system load average data and prints it in the debug pane","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fload-avg.png",[39,4879,4880,4883,4886],{},[42,4881,4882],{},"1-minute load average: Immediate system load",[42,4884,4885],{},"5-minute load average: Recent short-term trend",[42,4887,4888],{},"15-minute load average: Long-term system trend",[15,4890,4891],{},"If these values remain consistently high, your system may struggle under excessive demand, signaling a need for optimization or additional processing power.",[15,4893,4894],{},"With these metrics in place, you have a solid foundation for real-time system monitoring.",[399,4896,4898],{"id":4897},"sharing-data-across-different-node-red-instances","Sharing Data Across Different Node-RED Instances",[15,4900,4901],{},"Once we have the data, we must send it to the Node-RED instance handling visualization. Keeping the dashboard separate is essential for scalability. As the number of devices increases, a dedicated instance ensures we can monitor all of them from a single, centralized dashboard. This approach also makes management more efficient.",[15,4903,4904,4905,29],{},"To send data between multiple Node-RED instances we can use ",[63,4906,4908],{"href":4907},"\u002Fblog\u002F2024\u002F10\u002Fexploring-flowfuse-project-nodes\u002F","FlowFuse's Project Nodes",[15,4910,4911],{},"Before sending the data though, we combine all collected metrics into a single object for better organization and easier processing. Currently, each node sends its metrics as a separate message object. Merging them into a single object streamlines data handling and reduces message overhead.",[15,4913,4914],{},"Import the following flow and deploy it in the device instance. While I am not covering the step-by-step process here, the explanation below covers what the flow includes and how it works.",[15,4916,4917,4918,1150],{},"{% renderFlow %}\n",[1107,4919,4920,4921,4923,4924,4927,4928,4931,4932,4935,4936,4950,4951,4954,4955,4957],{},"{\"id\":\"6f655630d97bac87\",\"type\":\"cpu\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"msgCore\":false,\"msgOverall\":true,\"msgArray\":false,\"msgTemp\":false,\"x\":870,\"y\":460,\"wires\":[[\"d47b2da8024123bf\"]]},{\"id\":\"733dc91d94f03e49\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,4922],{},",\"repeat\":\"10\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":630,\"y\":520,\"wires\":[[\"6f655630d97bac87\",\"bcddc3aba82a12da\",\"7af11ed245ddf9c3\",\"d6d58c81cea93671\"]]},{\"id\":\"bcddc3aba82a12da\",\"type\":\"Memory\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"scale\":\"Gigabyte\",\"x\":860,\"y\":500,\"wires\":[[\"03ee32ddbd80d7a2\"]]},{\"id\":\"7af11ed245ddf9c3\",\"type\":\"Uptime\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"x\":860,\"y\":540,\"wires\":[[\"6117e013be2a11ac\"]]},{\"id\":\"d6d58c81cea93671\",\"type\":\"Loadavg\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"x\":860,\"y\":580,\"wires\":[[\"b759167c1663eb21\"]]},{\"id\":\"d47b2da8024123bf\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"CPU USAGE\",\"rules\":",[1107,4925,4926],{},"{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.CPU_USAGE\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1070,\"y\":460,\"wires\":[[\"7429fa970d1e3099\"]]},{\"id\":\"03ee32ddbd80d7a2\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"MEMORY USAGE\",\"rules\":",[1107,4929,4930],{},"{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.MEMORY_USAGE\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1090,\"y\":500,\"wires\":[[\"7429fa970d1e3099\"]]},{\"id\":\"6117e013be2a11ac\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"SYSTEM UPTIME\",\"rules\":",[1107,4933,4934],{},"{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.UPTIME\",\"pt\":\"msg\",\"to\":\"$floor(payload.uptime)\\t\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1090,\"y\":540,\"wires\":[[\"7429fa970d1e3099\"]]},{\"id\":\"b759167c1663eb21\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"LOAD AVERAGE\",\"rules\":",[1107,4937,4938,4939,4941,4942,4945,4946,4949],{},"{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.ONE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg",[1107,4940,1855],{},"\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIVE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg",[1107,4943,4944],{},"1","\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIFTEEN_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg",[1107,4947,4948],{},"2","\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1090,\"y\":580,\"wires\":[[\"7429fa970d1e3099\"]]},{\"id\":\"7429fa970d1e3099\",\"type\":\"join\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"mode\":\"custom\",\"build\":\"merged\",\"property\":\"data\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\n\",\"joinerType\":\"str\",\"useparts\":false,\"accumulate\":true,\"timeout\":\"\",\"count\":\"4\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":1310,\"y\":520,\"wires\":[[\"133d57048a7b057d\"]]},{\"id\":\"133d57048a7b057d\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"rules\":",[1107,4952,4953],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1490,\"y\":520,\"wires\":[[\"8123ad61cf50e61e\"]]},{\"id\":\"8123ad61cf50e61e\",\"type\":\"project link out\",\"z\":\"FFF0000000000001\",\"name\":\"project out 1\",\"mode\":\"link\",\"broadcast\":true,\"project\":\"28a809c6-b8f3-499f-bb20-e357c292b443\",\"topic\":\"${FF_DEVICE_NAME}\",\"x\":1670,\"y\":520,\"wires\":",[1107,4956],{},"}",[15,4959,4960],{},"Let's understand the flow.",[15,4962,4963,4964,853,4966,853,4969,4972,4973,4975,4976,4978],{},"In the flow above, four Change nodes are used, each connected to the output of the ",[19,4965,4737],{},[19,4967,4968],{},"Memory",[19,4970,4971],{},"Uptime",", and ",[19,4974,4861],{}," nodes. As mentioned earlier, these nodes provide their data separately as ",[924,4977,1227],{},". We use Change nodes to modify the message structure before sending the data to ensure a more structured and organized format.",[15,4980,4981,4982,4985,4986,29],{},"Next, a Join node merges the ",[924,4983,4984],{},"msg.data"," objects from all Change nodes into a single data object. After that, another Change node assigns this combined object to ",[924,4987,1227],{},[15,4989,4990],{},"The final combined object appears as shown in the image below:",[15,4992,4993,4997],{},[165,4994],{"alt":4995,"dataZoomable":103,"src":4996},"Combined object containing system data such as CPU usage, memory usage, uptime, and load average.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fcombine-object.png",[170,4998,4995],{},[15,5000,5001,5002,5005],{},"To share this data with other Node-RED instances, we use the ",[19,5003,5004],{},"Project Out"," node, which is available exclusively on FlowFuse. It works similarly to the Node-RED Link nodes, but allows for communication between multiple Instances, and uses MQTT in the background, so also beenfits with topic hierarchies for any communications.",[15,5007,5008,5009,5012],{},"In this Project node, we broadcast the message across all instances in the team using ",[924,5010,5011],{},"${FF_DEVICE_NAME}"," as the topic, an environment variable automatically created in all FlowFuse instances.",[15,5014,5015,5019],{},[165,5016],{"alt":5017,"dataZoomable":103,"src":5018},"Image showing the environment variables of Raspberry Pi devices with their values.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fenv.png",[170,5020,5017],{},[15,5022,5023],{},"Using environment variables as the topic enables the same flow to be used across multiple devices without modification, ensuring that each device utilizes its own environment variables (device name) and sends data under its respective topic.",[399,5025,5027],{"id":5026},"visualizing-data-with-the-flowfuse-dashboard","Visualizing Data with the FlowFuse Dashboard",[15,5029,5030],{},"Now that the data is being broadcasted, it can be used to build a simple dashboard that visualizes it with different types of charts.",[15,5032,5033,5037],{},[165,5034],{"alt":5035,"dataZoomable":103,"src":5036},"Dashboard monitoring device CPU usage, memory uptime, and load average","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fdashboard-monitoring-device.png",[170,5038,5035],{},[15,5040,5041,5042,5045],{},"Ensure that a separate ",[19,5043,5044],{},"Hosted Instance"," has been created in the same Team where the hardware is registered. This instance will be used to deploy the dashboard.",[1165,5047,5049],{"id":5048},"setting-up-the-data-source","Setting Up the Data Source",[271,5051,5052,5058,5061,5064,5070],{},[42,5053,5054,5055,1216],{},"Drag the ",[19,5056,5057],{},"Project In",[42,5059,5060],{},"Double-click on it and select \"Listen for broadcast messages from\".",[42,5062,5063],{},"Choose \"All instances and devices\" from the \"Source\" dropdown menu.",[42,5065,5066,5067,5069],{},"Enter the device name in the topic field, ensuring it matches exactly with the ",[924,5068,5011],{}," device environment variable.",[42,5071,297,5072,29],{},[19,5073,2453],{},[1165,5075,5077],{"id":5076},"memory-usage-visualization","Memory Usage Visualization",[271,5079,5080,5086,5106,5113,5130,5137,5142,5145,5148,5154,5158],{},[42,5081,5082,5083,5085],{},"Drag two ",[19,5084,1251],{}," nodes onto the canvas.",[42,5087,5088,5089,5091,5092,5094,5095],{},"Double-click on the first ",[19,5090,1251],{}," node. Set ",[924,5093,1227],{}," to:\n",[1756,5096,5100],{"className":5097,"code":5098,"language":5099,"meta":103,"style":103},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","payload.MEMORY_USAGE.totalmem - payload.MEMORY_USAGE.freemem\n","json",[924,5101,5102],{"__ignoreMap":103},[1107,5103,5104],{"class":1765,"line":1766},[1107,5105,5098],{"class":1779},[42,5107,1224,5108,5110,5111,29],{},[924,5109,1241],{}," to \"USED MEMORY\" and click ",[19,5112,2453],{},[42,5114,5115,5116,5118,5119,5094,5121],{},"Double-click on the second ",[19,5117,1251],{}," node, Set ",[924,5120,1227],{},[1756,5122,5124],{"className":5097,"code":5123,"language":5099,"meta":103,"style":103},"msg.payload.MEMORY_USAGE.freemem\n",[924,5125,5126],{"__ignoreMap":103},[1107,5127,5128],{"class":1765,"line":1766},[1107,5129,5123],{"class":1779},[42,5131,1224,5132,5134,5135,29],{},[924,5133,1241],{}," to \"Free Memory\" and click ",[19,5136,2453],{},[42,5138,1263,5139,5141],{},[19,5140,1552],{}," widget onto the canvas.",[42,5143,5144],{},"Double-click on the widget and create a new Group.",[42,5146,5147],{},"Set the chart type to \"Pie\" and action to \"Append\".",[42,5149,5150,5151,5153],{},"Set X to ",[924,5152,1241],{}," and leave Y empty.",[42,5155,297,5156,29],{},[19,5157,2453],{},[42,5159,5160],{},"Connect the nodes as follows:",[15,5162,5163],{},"Project In node → Change nodes → ui-chart widget",[1165,5165,5167],{"id":5166},"cpu-usage-visualization","CPU Usage Visualization",[271,5169,5170,5173,5193,5196,5199,5202,5205,5208,5211,5214,5217,5219],{},[42,5171,5172],{},"Drag a Change node onto the canvas.",[42,5174,5175,5176,5094,5178],{},"Double-click on the node. Set ",[924,5177,1227],{},[1756,5179,5181],{"className":5097,"code":5180,"language":5099,"meta":103,"style":103},"$round(payload.CPU_USAGE, 2)\n",[924,5182,5183],{"__ignoreMap":103},[1107,5184,5185,5188,5190],{"class":1765,"line":1766},[1107,5186,5187],{"class":1779},"$round(payload.CPU_USAGE, ",[1107,5189,4948],{"class":1854},[1107,5191,5192],{"class":1779},")\n",[42,5194,5195],{},"Click Done.",[42,5197,5198],{},"Drag a ui-gauge widget onto the canvas.",[42,5200,5201],{},"Double-click on the widget and create a new group.",[42,5203,5204],{},"Set the height and size.",[42,5206,5207],{},"Select \"3\u002F4 gauge\" with a rounded style.",[42,5209,5210],{},"Set the range from 0 to 100.",[42,5212,5213],{},"Add three segments with colors: 0 (Green), 50 (Yellow), 80 (Red).",[42,5215,5216],{},"Set the label to \"CPU\" and unit to %.",[42,5218,5195],{},[42,5220,5160],{},[15,5222,5223],{},"Project In node → Change node → ui-gauge widget",[1165,5225,5227],{"id":5226},"system-uptime-visualization","System Uptime Visualization",[271,5229,5230,5233,5236,5238,5240,5253,5255,5258,5261,5264,5267,5269],{},[42,5231,5232],{},"Drag the Humanizer node onto the canvas.",[42,5234,5235],{},"Double-click on the node and enter \"UPTIME\" in the input variable field.",[42,5237,5195],{},[42,5239,5172],{},[42,5241,5175,5242,5094,5244],{},[924,5243,1227],{},[1756,5245,5247],{"className":5097,"code":5246,"language":5099,"meta":103,"style":103},"msg.payload.humanized\n",[924,5248,5249],{"__ignoreMap":103},[1107,5250,5251],{"class":1765,"line":1766},[1107,5252,5246],{"class":1779},[42,5254,5195],{},[42,5256,5257],{},"Drag a ui-text widget onto the canvas.",[42,5259,5260],{},"Double-click on it and create a new group.",[42,5262,5263],{},"Select the correct layout.",[42,5265,5266],{},"Check the \"Apply Styles\" option and select the color, font, and size that best suits your needs.",[42,5268,5195],{},[42,5270,5160],{},[15,5272,5273],{},"Project In node → Humanizer node → Change node → ui-text widget",[15,5275,5276],{},"Below is the complete dashboard flow, which visualizes the system data we collected.",[15,5278,4917,5279,1150],{},[1107,5280,5281,5282,5285,5286,2545,5289,2549,5291,5293,5294,5297,5298,5301,5302,5305,5306,5308,5309,5311,5312,5314,5315,5318,5319,5322,5323,5326,5327,5330,5331,2545,5333,2549,5335,5337,5338,5340,5341,5344],{},"{\"id\":\"018657fd6a7e4237\",\"type\":\"project link in\",\"z\":\"797e084100cec864\",\"name\":\"project in 1\",\"project\":\"all\",\"broadcast\":true,\"topic\":\"MacOS\",\"x\":80,\"y\":260,\"wires\":[[\"1335f4283b6bac10\",\"acd675febbdadc6f\",\"f875ddcc0de1c40f\",\"95e3d356bd589be7\",\"7cde186dd1601efb\",\"6448cab78573bf39\",\"d0e232ec780650fa\"]]},{\"id\":\"1335f4283b6bac10\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"Free Memory\",\"rules\":",[1107,5283,5284],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.MEMORY_USAGE.freemem\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"Free Memory\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":270,\"y\":160,\"wires\":[[\"eed0837fccf2639d\"]]},{\"id\":\"eed0837fccf2639d\",\"type\":\"ui-chart\",\"z\":\"797e084100cec864\",\"group\":\"a54eed4c7110dfb5\",\"name\":\"Memory Usage\",\"label\":\"X - msg.topic, Series - msg.series\",\"order\":1,\"chartType\":\"pie\",\"category\":\"Pie\",\"categoryType\":\"str\",\"xAxisLabel\":\"\",\"xAxisProperty\":\"topic\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"radial\",\"xAxisFormat\":\"\",\"xAxisFormatType\":\"auto\",\"xmin\":\"\",\"xmax\":\"\",\"yAxisLabel\":\"\",\"yAxisProperty\":\"\",\"yAxisPropertyType\":\"property\",\"ymin\":\"\",\"ymax\":\"\",\"bins\":\"\",\"action\":\"append\",\"stackSeries\":false,\"pointShape\":\"circle\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":",[1107,5287,5288],{},"\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"",[1107,5290,2548],{},[1107,5292,2552],{},",\"gridColorDefault\":true,\"width\":\"3\",\"height\":\"3\",\"className\":\"\",\"interpolation\":\"linear\",\"x\":520,\"y\":140,\"wires\":[[]]},{\"id\":\"acd675febbdadc6f\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"Used Memory\",\"rules\":",[1107,5295,5296],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.MEMORY_USAGE.totalmem - payload.MEMORY_USAGE.freemem\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"Used Memory\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":280,\"y\":120,\"wires\":[[\"eed0837fccf2639d\"]]},{\"id\":\"f875ddcc0de1c40f\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"CPU Overall Usage\",\"rules\":",[1107,5299,5300],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$round(payload.CPU_USAGE, 2)\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"CPU Overall Usage\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":290,\"y\":260,\"wires\":[[\"a777eb57611ddd60\"]]},{\"id\":\"a777eb57611ddd60\",\"type\":\"ui-gauge\",\"z\":\"797e084100cec864\",\"name\":\"\",\"group\":\"0d1f7e47031c74c1\",\"order\":1,\"width\":\"4\",\"height\":\"5\",\"gtype\":\"gauge-34\",\"gstyle\":\"rounded\",\"title\":\"CPU \",\"units\":\"units\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":",[1107,5303,5304],{},"{\"from\":\"0\",\"color\":\"#5cd65c\"},{\"from\":\"4\",\"color\":\"#ffc800\"},{\"from\":\"7\",\"color\":\"#ea5353\"}",",\"min\":0,\"max\":10,\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":490,\"y\":260,\"wires\":",[1107,5307],{},"},{\"id\":\"95e3d356bd589be7\",\"type\":\"humanizer\",\"z\":\"797e084100cec864\",\"name\":\"\",\"input\":\"UPTIME\",\"x\":290,\"y\":340,\"wires\":[[\"9d237ff676ad6083\"]]},{\"id\":\"79e5bdfa309d27ea\",\"type\":\"ui-text\",\"z\":\"797e084100cec864\",\"group\":\"3827fa7650fa2fa1\",\"order\":1,\"width\":\"4\",\"height\":\"5\",\"name\":\"Uptime\",\"label\":\"\",\"format\":\"",[2526,5310],{"value":1227},"\",\"layout\":\"col-center\",\"style\":true,\"font\":\"\",\"fontSize\":\"99\",\"color\":\"#0056d6\",\"wrapText\":false,\"className\":\"\",\"x\":680,\"y\":340,\"wires\":",[1107,5313],{},"},{\"id\":\"9d237ff676ad6083\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"\",\"rules\":",[1107,5316,5317],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.humanized\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":480,\"y\":340,\"wires\":[[\"79e5bdfa309d27ea\"]]},{\"id\":\"7cde186dd1601efb\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"One Minute (LOAD_AVERAGE)\",\"rules\":",[1107,5320,5321],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.LOAD_AVERAGE.ONE_MIN\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"One Minute\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":330,\"y\":400,\"wires\":[[\"b5bf2c8c93abb5a9\"]]},{\"id\":\"6448cab78573bf39\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"Five Minute (LOAD_AVERAGE)\",\"rules\":",[1107,5324,5325],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.LOAD_AVERAGE.FIVE_MIN\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"Five Minute\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":330,\"y\":440,\"wires\":[[\"b5bf2c8c93abb5a9\"]]},{\"id\":\"d0e232ec780650fa\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"Fifteen Minute (LOAD_AVERAGE)\",\"rules\":",[1107,5328,5329],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.LOAD_AVERAGE.FIFTEEN_MIN\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"Fifteen Minute\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":340,\"y\":480,\"wires\":[[\"b5bf2c8c93abb5a9\"]]},{\"id\":\"b5bf2c8c93abb5a9\",\"type\":\"ui-chart\",\"z\":\"797e084100cec864\",\"group\":\"0c78dcb3aefb38a8\",\"name\":\"LOAD AVERAGE\",\"label\":\"chart\",\"order\":1,\"chartType\":\"line\",\"category\":\"topic\",\"categoryType\":\"msg\",\"xAxisLabel\":\"\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"timestamp\",\"xAxisType\":\"time\",\"xAxisFormat\":\"\",\"xAxisFormatType\":\"auto\",\"xmin\":\"\",\"xmax\":\"\",\"yAxisLabel\":\"\",\"yAxisProperty\":\"payload\",\"yAxisPropertyType\":\"msg\",\"ymin\":\"\",\"ymax\":\"\",\"bins\":10,\"action\":\"append\",\"stackSeries\":false,\"pointShape\":\"dash\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":",[1107,5332,2544],{},[1107,5334,2548],{},[1107,5336,2552],{},",\"gridColorDefault\":true,\"width\":\"12\",\"height\":\"6\",\"className\":\"\",\"interpolation\":\"linear\",\"x\":610,\"y\":440,\"wires\":[[]]},{\"id\":\"a54eed4c7110dfb5\",\"type\":\"ui-group\",\"name\":\"Memory Usage\",\"page\":\"d0621b8f20aee671\",\"width\":\"3\",\"height\":\"3\",\"order\":3,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"0d1f7e47031c74c1\",\"type\":\"ui-group\",\"name\":\"CPU Usage\",\"page\":\"d0621b8f20aee671\",\"width\":\"4\",\"height\":\"5\",\"order\":2,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"3827fa7650fa2fa1\",\"type\":\"ui-group\",\"name\":\"SYSTEM UPTIME\",\"page\":\"d0621b8f20aee671\",\"width\":\"5\",\"height\":\"5\",\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"0c78dcb3aefb38a8\",\"type\":\"ui-group\",\"name\":\"Load Average\",\"page\":\"d0621b8f20aee671\",\"width\":\"12\",\"height\":1,\"order\":4,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"d0621b8f20aee671\",\"type\":\"ui-page\",\"name\":\"Mac OS\",\"ui\":\"6c8450c52cafa145\",\"path\":\"\u002Fmacos\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"5075a7d8e4947586\",\"breakpoints\":",[1107,5339,2556],{},",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"6c8450c52cafa145\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,5342,5343],{},"\"ui-notification\",\"ui-control\"",",\"showPathInSidebar\":false,\"headerContent\":\"page\",\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\",\"showReconnectNotification\":true,\"notificationDisplayTime\":1,\"showDisconnectNotification\":true},{\"id\":\"5075a7d8e4947586\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094CE\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}}",[399,5346,5348],{"id":5347},"scaling-device-monitoring-with-flowfuse","Scaling Device Monitoring with FlowFuse",[15,5350,5351],{},"Now that we have learned how to monitor a single device, built a flow to gather system data, and created a dashboard to visualize those metrics, the real challenge arises when scaling up to thousands or even tens of thousands of devices. Manually creating a system data-gathering flow for each device would be impractical. However, FlowFuse can automate this process in less than five minutes. Let's see how.",[1165,5353,5355],{"id":5354},"creating-device-group","Creating Device Group",[271,5357,5358],{},[42,5359,5360],{},"Navigate to the FlowFuse platform and go to the Application where your devices are and where you want to create a group. Ensure that all the devices you want to monitor are part of this application.",[15,5362,5363,5367,5370],{},[165,5364],{"alt":5365,"dataZoomable":103,"src":5366},"Showing the option to switch to \"Device Groups\" and the \"Add Device Group\" button.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Foption-add-device-group.png",[5368,5369],"br",{},[170,5371,5365],{},[271,5373,5374],{},[42,5375,5376],{},"Click on \"Device Groups\" from the top menu. Next, click on the \"Add Device Group\" button. In the newly opened window, enter a group name and description, then click \"Create\".",[15,5378,5379,5383,5385],{},[165,5380],{"alt":5381,"dataZoomable":103,"src":5382},"Form to Create a Device Group: Enter the group name and description ","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fdevice-group-form-create.png",[5368,5384],{},[170,5386,5387],{},"Form to Create a Device Group: Enter the group name and description",[271,5389,5390],{"start":107},[42,5391,5392],{},"Click on the newly created group and then click the \"Edit\" button at the top-right.",[15,5394,5395,5399,5401],{},[165,5396],{"alt":5397,"dataZoomable":103,"src":5398},"Image showing the edit button to be clicked on.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fedit-device-group.png",[5368,5400],{},[170,5402,5397],{},[271,5404,5405],{"start":294},[42,5406,5407],{},"Next, in the left-side container, you will see a list of all available devices in your application. Select the devices you want to add to the group (make sure to add only the devices that require the deployment of the flow built to gather system metrics). Click the \"Add Devices\" button at the top-right of that container, and then click \"Save Changes\". Once done, you will see all added devices in the right-side container, confirming that they have been successfully added to the group.",[15,5409,5410,5414,5416],{},[165,5411],{"alt":5412,"dataZoomable":103,"src":5413},"Interface to select the devices that need to be added to the group, along with the 'Add Devices' button.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fdevice-group-device-adding.png",[5368,5415],{},[170,5417,5418],{},"Interface to select the devices that must be added to the group, along with the 'Add Devices' button.",[15,5420,5421,5425,5427],{},[165,5422],{"alt":5423,"dataZoomable":103,"src":5424},"Showing the selected devices we chose to add, along with the 'Save Changes' button.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fsave-changes-to-add-devices.png",[5368,5426],{},[170,5428,5423],{},[1165,5430,5432],{"id":5431},"creating-snapshot","Creating Snapshot",[271,5434,5435,5438],{},[42,5436,5437],{},"Navigate to the Remote Instance on which we developed the flow to monitor performance. Switch to \"Version History\" by clicking on \"Version History\" from the top.",[42,5439,5440],{},"Go to the Snapshots tab and create a new snapshot by clicking the \"Create Snapshot\" button. Enter details such as the name and description. While making the snapshot, ensure the \"Set as Target\" option is checked before clicking \"Create\". Enabling this option sets the created snapshot as the device’s active snapshot. Later, this snapshot will be used for deployment on devices within the device group via the DevOps pipeline.",[15,5442,5443,5447,5449],{},[165,5444],{"alt":5445,"dataZoomable":103,"src":5446},"Showing the option to switch to \"Version history\" and the \"Create Snapshot\" button.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fcreate-snapshot.png",[5368,5448],{},[170,5450,5445],{},[15,5452,5453,5457,5459],{},[165,5454],{"alt":5455,"dataZoomable":103,"src":5456},"\"Showing the form to create a snapshot and the \"Set as Target\" option.\"","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fset-active-snapshot.png",[5368,5458],{},[170,5460,5461],{},"Showing the form to create a snapshot and the \"Set as Target\" option.",[15,5463,5464,5465,29],{},"If you want to learn more about snapshots, you can read our article ",[63,5466,5468],{"href":5467},"\u002Fblog\u002F2024\u002F09\u002Fnode-red-version-control-with-snapshots\u002F","Using Snapshots for Version Control in Node-RED with FlowFuse",[1165,5470,5472],{"id":5471},"creating-a-devops-pipeline","Creating a DevOps Pipeline",[271,5474,5475],{},[42,5476,5477],{},"Navigate to the application where the devices were added and the device group was created. Switch to the \"Pipelines\" tab at the top, then click \"Add Pipeline\". In the newly opened window, enter a pipeline name.",[15,5479,5480,5484,5486],{},[165,5481],{"alt":5482,"dataZoomable":103,"src":5483},"Image showing the 'Add Pipeline' button.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fadd-pipeline-button.png",[5368,5485],{},[170,5487,5482],{},[15,5489,5490,5494,5496],{},[165,5491],{"alt":5492,"dataZoomable":103,"src":5493},"Image showing the form to create a pipeline by entering a name.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fpipeline-creation-form.png",[5368,5495],{},[170,5497,5492],{},[271,5499,5500],{"start":107},[42,5501,5502],{},"In the newly added pipeline, click \"Add Stage\".",[15,5504,5505,5509,5511],{},[165,5506],{"alt":5507,"dataZoomable":103,"src":5508},"Image showing the button to add a stage.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fadding-stage-button.png",[5368,5510],{},[170,5512,5507],{},[271,5514,5515],{"start":294},[42,5516,5517],{},"In the newly opened window, select \"Remote Instance\" as the stage type, enter a stage name, and select the device where the flow was previously built for a single device. Under \"Action,\" select \"Use active snapshot\" and click \"Add Stage\".",[15,5519,5520,5524,5526],{},[165,5521],{"alt":5522,"dataZoomable":103,"src":5523},"Image showing the form to add a stage, where a stage is being added for a Raspberry Pi remote instance.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Frpi-stage.png",[5368,5525],{},[170,5527,5522],{},[271,5529,5530],{"start":104},[42,5531,5532],{},"To add another stage, select \"Device Group\" as the stage type, enter a stage name, choose the previously created device group, and click \"Add Stage.\"",[15,5534,5535,5539,5541],{},[165,5536],{"alt":5537,"dataZoomable":103,"src":5538},"Image showing the form to add a stage, where a stage is being added for a Device Group.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fproduction-with-deviec-group.png",[5368,5540],{},[170,5542,5537],{},[271,5544,5545],{"start":1868},[42,5546,5547],{},"Before moving further, ensure all devices are in fleet mode.",[15,5549,5550,5554,5556],{},[165,5551],{"alt":5552,"dataZoomable":103,"src":5553},"Image showing the fleet mode status of the device (disabling the developer mode option will set the device to fleet mode).","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Ffleet-mode.png",[5368,5555],{},[170,5557,5552],{},[271,5559,5560],{"start":1896},[42,5561,5562],{},"Once both stages are added, click the 'Run Pipeline' button for the first stage. Running the pipeline will deploy the active snapshot to the devices in the device group, including all settings, environment variables, and flows of that instance. Whether the device group has two devices or thousands, the deployment will be completed efficiently and quickly.",[15,5564,5565,5566,29],{},"To learn more about DevOps pipelines, read the article: ",[63,5567,5568],{"href":851},"Creating and Automating DevOps Pipelines for Node-RED in Industrial Environments",[15,5570,5571,5572,5576],{},"Now, you have the system data of all devices broadcasted on the topic and the device name. To monitor each device, go to the dashboard instance, copy the flow, and create copies for each device. Ensure that you replace the topic with the corresponding device name. Additionally, create a separate page for each device, assign them to separate groups, and correctly move all copied widgets into the appropriate groups. Alternatively, follow ",[63,5573,5575],{"href":5574},"#visualizing-data-with-the-flowfuse-dashboard","these steps"," again for each device, and you will have a centralized dashboard monitoring thousands of devices live.",[441,5578],{"videoid":4624,"params":464,"style":4625,"title":3470},[31,5580,1425],{"id":1424},[15,5582,5583],{},"Building a monitoring flow in Node-RED is simple. It allows you to track key system metrics like CPU usage, memory, and uptime with minimal effort. Its low-code Interface makes it easy to create and deploy monitoring solutions quickly.",[15,5585,5586],{},"However, manually deploying this monitoring flow across 10,000 or even 100,000 devices can be a complex and time-consuming task. This is where FlowFuse makes a difference. With features like Device Groups and DevOps pipelines, you can deploy your application from a single device or hosted Node-RED instance to thousands of devices with just a single click. FlowFuse also provides powerful tools for scaling, managing, and monitoring industrial operations, making large-scale deployments more efficient and hassle-free.",[2582,5588,5589],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":103,"searchDepth":104,"depth":104,"links":5591},[5592,5593,5614],{"id":4628,"depth":107,"text":4629},{"id":4644,"depth":107,"text":4645,"children":5594},[5595,5596,5602,5603,5609],{"id":819,"depth":294,"text":820},{"id":4716,"depth":294,"text":4717,"children":5597},[5598,5599,5600,5601],{"id":4726,"depth":104,"text":4727},{"id":4772,"depth":104,"text":4773},{"id":4818,"depth":104,"text":4819},{"id":4850,"depth":104,"text":4851},{"id":4897,"depth":294,"text":4898},{"id":5026,"depth":294,"text":5027,"children":5604},[5605,5606,5607,5608],{"id":5048,"depth":104,"text":5049},{"id":5076,"depth":104,"text":5077},{"id":5166,"depth":104,"text":5167},{"id":5226,"depth":104,"text":5227},{"id":5347,"depth":294,"text":5348,"children":5610},[5611,5612,5613],{"id":5354,"depth":104,"text":5355},{"id":5431,"depth":104,"text":5432},{"id":5471,"depth":104,"text":5472},{"id":1424,"depth":107,"text":1425},"2025-02-21","Learn how to monitor system health and performance with Node-RED. Track CPU usage, memory, and other key metrics, and efficiently scale device monitoring with FlowFuse to thousands of devices.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fmonitoring-device-health-and-performance-at-scale.png",{"keywords":5619,"excerpt":5620},"real-time device monitoring, FlowFuse for IoT monitoring, scalable edge device monitoring, remote device performance tracking, centralized device monitoring dashboard, optimizing IoT device health, real-time performance tracking with Node-RED, remote monitoring for industrial automation",{"type":12,"value":5621},[5622],[15,5623,4612,5624,4618],{},[63,5625,4617],{"href":4615,"rel":5626},[67],"\u002Fblog\u002F2025\u002F02\u002Fmonitoring-system-health-performance-scale-flowfuse",{"title":4606,"description":5616},{"loc":5627},"blog\u002F2025\u002F02\u002Fmonitoring-system-health-performance-scale-flowfuse","Track and Optimize Edge Device Performance with Node-RED and FlowFuse.",[138,10],"DUHBw4v7Gdr_grlePvcPXAl67TzhS7EyDRem6k_ksDw",{"id":5635,"title":5636,"authors":5637,"body":5638,"cta":3,"date":6061,"description":6062,"extension":116,"image":6063,"lastUpdated":6064,"meta":6065,"navigation":129,"path":6071,"seo":6072,"sitemap":6073,"stem":6074,"subtitle":6075,"tags":6076,"tldr":6077,"video":3,"__hash__":6078},"blog\u002Fblog\u002F2025\u002F02\u002Finteracting-with-arduino-using-node-red.md","Interacting with Arduino using Node-RED",[144],{"type":12,"value":5639,"toc":6049},[5640,5643,5646,5649,5651,5654,5668,5672,5675,5679,5682,5685,5696,5700,5708,5711,5716,5730,5738,5741,5745,5748,5751,5756,5769,5772,5777,5788,5796,5801,5804,5808,5811,5824,5827,5841,5848,5858,5868,5871,5874,5889,5896,5900,5903,5919,5929,5934,5943,5946,5949,5957,5961,5964,5967,5975,5983,5988,5996,6007,6010,6013,6036,6038,6040,6043,6046],[15,5641,5642],{},"Arduino is a popular open-source platform that lets you build cool electronics projects. It’s affordable and flexible, with lots of different boards and sensors to choose from. However, unlike some other boards, Arduino doesn’t have built-in internet connectivity, which can make remote control a bit tricky. Plus, it usually requires some coding to make things work.",[15,5644,5645],{},"In this guide, I’ll show you how to control and automate your Arduino remotely using Node-RED and FlowFuse, all without writing any code. We’ll use the Firmata protocol to make it easy to send commands and get data from your Arduino. By the end of this tutorial, you’ll have a simple automation setup that you can control from anywhere. Just keep in mind, the Arduino will need to be connected to the device running Node-RED!",[441,5647],{"videoid":5648,"params":464,"style":4625,"title":3470},"FTuxOy16nwo",[31,5650,820],{"id":819},[15,5652,5653],{},"To follow this tutorial, you'll need the following:",[39,5655,5656,5659,5662,5665],{},[42,5657,5658],{},"Arduino Board: The hardware you'll be using for this project.",[42,5660,5661],{},"USB cable: To connect the Arduino to your computer.",[42,5663,5664],{},"Arduino IDE: Installed and set up to program your Arduino. Download the Arduino IDE if you haven't already done so. we will be using this for initial firmata implementation, not for programming",[42,5666,5667],{},"FlowFuse Account: You will need a FlowFuse instance running on the device connected to the Arduino. FlowFuse allows you to access that remote instance, build flows, create remotely available dashboards, collaborate with your team on the instance, provide robust security, and much more.",[31,5669,5671],{"id":5670},"getting-started-with-arduino-and-node-red","Getting Started with Arduino and Node-RED",[15,5673,5674],{},"In this section, we’ll set up Node-RED on FlowFuse and download the Firmata protocol setup on the Arduino using Arduino IDE. We will also create a flow that will control an LED on the Arduino and read input data. We will later control the LED based on object detection using an object sensor to make it more interesting. If you don’t have the sensor, don't worry, you can still follow the article. The goal of this example is to demonstrate both reading and writing operations, as well as build an automation flow that reacts to input.",[399,5676,5678],{"id":5677},"step-1-running-node-red-on-the-device-connected-to-arduino","Step 1: Running Node-RED on the Device connected to Arduino",[15,5680,5681],{},"To begin, you need to run Node-RED on the device connected to your Arduino, whether it is a Raspberry Pi, Windows, or Linux system. However, simply running Node-RED locally is not sufficient if you require remote access. Setting up a server, securing it, and ensuring accessibility can be time-consuming and complex.",[15,5683,5684],{},"Using the FlowFuse device agent simplifies this process. It allows you to remotely access and manage your Node-RED instance without the need for extensive configuration or security management. This approach ensures a more efficient and secure deployment, enabling you to focus on building automation solutions.",[15,5686,5687,5688,5690,5691,5695],{},"For a step-by-step guide on installing and running the FlowFuse device agent, refer to the official documentation: ",[63,5689,4672],{"href":4671},". By the way, we also offer a ",[63,5692,5694],{"href":5693},"\u002Fblog\u002F2024\u002F12\u002Fflowfuse-release-2-12\u002F","free tier"," that lets you manage up to two edge devices for free. [Sign up today]({% include \"sign-up-url.njk\" %}?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Interacting%20with%20Arduino%20using%20Node-RED)!",[399,5697,5699],{"id":5698},"step-2-downloading-firmata-protocol-setup-to-arduino","Step 2: Downloading Firmata protocol setup to Arduino.",[15,5701,5702,5707],{},[63,5703,5706],{"href":5704,"rel":5705},"https:\u002F\u002Fgithub.com\u002Ffirmata\u002Fprotocol",[67],"Firmata"," is a protocol for communicating between an Arduino (and other microcontrollers) and the host computer, providing direct access to the IO pins.",[15,5709,5710],{},"Now, let's download the setup to the Arduino. Before proceeding, ensure your Arduino is connected to your laptop or computer via the correct USB cable. The USB connection is essential for uploading the code (sketch) to the Arduino and will also be used by Firmata for communication later.",[15,5712,5713],{},[19,5714,5715],{},"Setting up Arduino IDE and Download the setup from examples:",[271,5717,5718,5721,5724,5727],{},[42,5719,5720],{},"Open the Arduino IDE on your computer.",[42,5722,5723],{},"Ensure you have selected the correct board and port in the Tools menu. Since I am using an Arduino Uno 3 board, I’ve selected the Arduino Uno board.",[42,5725,5726],{},"Go to File → Examples → Firmata, and then click on StandardFirmata. This will open the Firmata setup code.",[42,5728,5729],{},"Click the Upload button in the Arduino IDE to upload the setup to your Arduino board.",[15,5731,5732,5736],{},[165,5733],{"alt":5734,"dataZoomable":103,"src":5735},"Importing Standard Firmata setup sketch from examples in Arduino IDE","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Ffirmata-import.png",[170,5737,5734],{},[15,5739,5740],{},"Once the upload is complete, the Arduino is ready to communicate via the Firmata protocol.",[399,5742,5744],{"id":5743},"step-3-connecting-node-red-to-arduino-via-serial-communication","Step 3: Connecting Node-RED to Arduino via Serial Communication",[15,5746,5747],{},"As mentioned earlier, Firmata typically works over a serial connection (such as USB), enabling communication between the Arduino board and your Node-RED instance. The serial communication allows Node-RED to send commands to the Arduino and receive data from it.",[15,5749,5750],{},"First, we will need to install a node that will enable communication between Node-RED and the Arduino via Firmata.",[15,5752,5753],{},[19,5754,5755],{},"Installing Arduino Node",[271,5757,5758,5761,5764,5767],{},[42,5759,5760],{},"Open the main menu by clicking the three horizontal lines in the top-right corner.",[42,5762,5763],{},"Click \"Manage Palette\" from the menu.",[42,5765,5766],{},"Switch to the \"Install\" tab and type \"node-red-node-arduino\" in the search field.",[42,5768,3289],{},[15,5770,5771],{},"After installing the required node for Arduino, we will set up the flow in Node-RED to establish a connection with the Arduino.",[15,5773,5774],{},[19,5775,5776],{},"Establishing Connection with Arduino",[271,5778,5779,5782,5785],{},[42,5780,5781],{},"Drag any Arduino node onto the canvas",[42,5783,5784],{},"Double-click the node to open its configuration window.",[42,5786,5787],{},"In the new window that opens, enter the port name for your Arduino (for example, COM5 on Windows or \u002Fdev\u002FttyUSB0 on Linux\u002FmacOS). You can find the correct port in the Arduino IDE or your system’s device manager.",[15,5789,5790,5794],{},[165,5791],{"alt":5792,"dataZoomable":103,"src":5793},"Adding the port to the Arduino node","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fserial-port-config.png",[170,5795,5792],{},[271,5797,5798],{"start":104},[42,5799,5800],{},"Click and deploy by clicking the top-right deploy button.",[15,5802,5803],{},"Once deployed, after a few seconds, the node will establish a connection with the Arduino board. You should see a green square below the node, indicating that the connection is successful and the status is \"Connected.\"",[399,5805,5807],{"id":5806},"step-4-sending-commands-to-arduino","Step 4: Sending Commands to Arduino",[15,5809,5810],{},"In this section, I'll show you how to send commands to your Arduino. For this practical demonstration, we will control the default Arduino LED, which is typically connected to pin 13.",[271,5812,5813,5816,5818,5821],{},[42,5814,5815],{},"Drag the Arduino-out node onto the canvas.",[42,5817,5784],{},[42,5819,5820],{},"Select the correct configuration (serial port) that you set up in Step 3.",[42,5822,5823],{},"Next, choose the type of pin you want to interact with (e.g., digital, analog, servo, etc.). Since in this example we will be interacting with an LED, which only requires on or off commands, I’ve selected Digital (0\u002F1).",[15,5825,5826],{},"Based on the type of data you want to send, select the appropriate pin type:",[39,5828,5829,5832,5835,5838],{},[42,5830,5831],{},"Digital (0\u002F1) – accepts 0, 1",[42,5833,5834],{},"Analog (0-255) (PWM) – accepts Integer values from 0 to 255",[42,5836,5837],{},"Servo (0-180) – accepts Integer values from 0 to 180",[42,5839,5840],{},"String – to send a string to the Arduino",[271,5842,5843,5846],{"start":1868},[42,5844,5845],{},"Once you've configured the pin type, enter the pin number (e.g., 13 for the built-in LED).",[42,5847,5195],{},[15,5849,5850,5854,5856],{},[165,5851],{"alt":5852,"dataZoomable":103,"src":5853},"Configuring Arduino-out node","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fcontrol-led.png",[5368,5855],{},[170,5857,5852],{},[271,5859,5860,5863,5866],{"start":1623},[42,5861,5862],{},"Drag two Inject nodes onto the canvas. For one Inject node, set the payload to true (to turn the LED on), and for the other, set the payload to false (to turn the LED off).",[42,5864,5865],{},"Connect the output of each Inject node to the input of the Arduino-out node.",[42,5867,3583],{},[15,5869,5870],{},"Now, you can turn the LED on and off by clicking the inject buttons. Instead of using the inject node, you can also use the FlowFuse dashboard to build an interactive dashboard. The dashboard will allow you to control the LED directly from a web interface.",[15,5872,5873],{},"Below is the flow that allows you to control the LED connected to pin 13.",[15,5875,4917,5876,1150],{},[1107,5877,5878,5879,5881,5882,5885,5886,5888],{},"{\"id\":\"fbe0eb6547cbfed7\",\"type\":\"arduino out\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"pin\":\"13\",\"state\":\"OUTPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":739.8345947265625,\"y\":645.8272094726562,\"wires\":",[1107,5880],{},"},{\"id\":\"e222d2df8c62483d\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"Turn LED on\",\"props\":",[1107,5883,5884],{},"{\"p\":\"payload\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"true\",\"payloadType\":\"bool\",\"x\":530,\"y\":580,\"wires\":[[\"fbe0eb6547cbfed7\"]]},{\"id\":\"c82bd0280ee45b3b\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"Turn LED off\",\"props\":",[1107,5887,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"false\",\"payloadType\":\"bool\",\"x\":530,\"y\":680,\"wires\":[[\"fbe0eb6547cbfed7\"]]},{\"id\":\"d7663aaf.47194\",\"type\":\"arduino-board\",\"device\":\"COM5\"}",[15,5890,5891,5892,5895],{},"If you're interested in learning how to create a dashboard, you can refer to the ",[63,5893,5894],{"href":3671},"Getting Started Guide",". It will help clarify basic dashboard concepts and guide you through building a simple dashboard interface.",[399,5897,5899],{"id":5898},"step-5-receiving-inputs-from-the-arduino","Step 5: Receiving Inputs from the Arduino",[15,5901,5902],{},"In this step, we’ll focus on receiving inputs from the Arduino to Node-RED. For this practical demonstration, we will use the input from the IR object detection sensor connected to the Arduino.",[271,5904,5905,5908,5910,5913,5916],{},[42,5906,5907],{},"Drag the Arduino-in node onto the canvas.",[42,5909,5784],{},[42,5911,5912],{},"Select the serial port to which your Arduino is connected.",[42,5914,5915],{},"Based on the input type, choose the correct pin type (e.g., digital, analog). Since we are using an IR sensor, which typically provides a digital output, select the \"Digital pin\" type.",[42,5917,5918],{},"Enter the pin number (e.g., pin 9, if your sensor is connected to pin 9 on the Arduino).",[15,5920,5921,5925,5927],{},[165,5922],{"alt":5923,"dataZoomable":103,"src":5924},"Configuring Arduino-in node","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fread-sensor-data.png",[5368,5926],{},[170,5928,5923],{},[15,5930,5931],{},[170,5932,5933],{},"Note: You cannot use the same pin for both output and input on the Arduino simultaneously. Ensure the pin you use for input (like the IR sensor) is separate from the pin you're using for output (like the LED).",[271,5935,5936,5938,5941],{"start":1896},[42,5937,5195],{},[42,5939,5940],{},"Drag the Debug node onto the canvas and connect its input to the output of the Arduino-in node.",[42,5942,3583],{},[15,5944,5945],{},"Now, the Arduino will send the sensor input data to Node-RED. If a change is detected in the input, the Arduino will output that input, and you will see it in the debug panel.",[15,5947,5948],{},"Below, I have provided the flow that reads the IR object detection sensor data connected to pin 9, in case you need it.",[15,5950,4917,5951,1150],{},[1107,5952,5953,5954,5956],{},"{\"id\":\"2b871fb5d0923355\",\"type\":\"arduino in\",\"z\":\"FFF0000000000001\",\"name\":\"Read Sensor Data\",\"pin\":\"9\",\"state\":\"INPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":170,\"y\":360,\"wires\":[[\"e3e52e51a2e4e058\"]]},{\"id\":\"e3e52e51a2e4e058\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":360,\"y\":360,\"wires\":",[1107,5955],{},"},{\"id\":\"d7663aaf.47194\",\"type\":\"arduino-board\",\"device\":\"COM5\"}",[399,5958,5960],{"id":5959},"step-6-creating-an-automation-flow","Step 6: Creating an Automation Flow",[15,5962,5963],{},"Now that you've learned how to send commands and read inputs from the Arduino, let’s move on to creating an automation flow. This section aims to show you how to program the Arduino without writing a single line of code, using only Node-RED.",[15,5965,5966],{},"The idea is to trigger an action, such as turning on an LED, when the sensor detects an object.",[271,5968,5969,5972],{},[42,5970,5971],{},"Drag a Switch node onto the canvas.",[42,5973,5974],{},"Double-click on the Switch node to open its configuration window and add the following conditions:",[39,5976,5977,5980],{},[42,5978,5979],{},"msg.payload == 1",[42,5981,5982],{},"msg.payload == 0",[271,5984,5985],{"start":294},[42,5986,5987],{},"Click Done to save the configuration.",[15,5989,5990,5994],{},[165,5991],{"alt":5992,"dataZoomable":103,"src":5993},"Adding conditions in the Switch node to check if the object is detected or not.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Fswitch.png",[170,5995,5992],{},[271,5997,5998,6001,6004],{"start":104},[42,5999,6000],{},"Connect the Switch node's input to the Arduino-in node's output.",[42,6002,6003],{},"Next, drag two Change nodes onto the canvas. Set the payload to false for the first Change node and true for the second Change node.",[42,6005,6006],{},"Connect the first output of the Switch node to the input of the first Change node, and the second output of the Switch node to the input of the second Change node.",[15,6008,6009],{},"Since IR object detection sensors output a LOW (0) signal when an object is detected and a HIGH (1) signal when no object is detected, this flow will turn the LED on when an object is detected (when msg.payload is equal to 0) and turn the LED off  object is not detected (when msg.payload is equal to 1).",[15,6011,6012],{},"Below, I have provided the complete flow of how we read the IR object detection sensor data and control the LED in response to the input. I have also included a video demo.",[15,6014,4917,6015,1150],{},[1107,6016,6017,6018,6021,6022,6025,6026,6029,6030,6033,6034,5956],{},"{\"id\":\"2b871fb5d0923355\",\"type\":\"arduino in\",\"z\":\"FFF0000000000001\",\"name\":\"Read Sensor Data\",\"pin\":\"9\",\"state\":\"INPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":130,\"y\":460,\"wires\":[[\"8ad3d1504ba05942\"]]},{\"id\":\"49b04d8b6f015846\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"rules\":",[1107,6019,6020],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"true\",\"tot\":\"bool\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":530,\"y\":480,\"wires\":[[\"d6e251e983f908ac\"]]},{\"id\":\"8f56a3d6ac64e87c\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"rules\":",[1107,6023,6024],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"false\",\"tot\":\"bool\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":530,\"y\":440,\"wires\":[[\"d6e251e983f908ac\"]]},{\"id\":\"8ad3d1504ba05942\",\"type\":\"switch\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":",[1107,6027,6028],{},"{\"t\":\"eq\",\"v\":\"1\",\"vt\":\"num\"},{\"t\":\"else\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":330,\"y\":460,\"wires\":[[\"8f56a3d6ac64e87c\"],",[1107,6031,6032],{},"\"49b04d8b6f015846\"","]},{\"id\":\"d6e251e983f908ac\",\"type\":\"arduino out\",\"z\":\"FFF0000000000001\",\"name\":\"Control LED\",\"pin\":\"13\",\"state\":\"OUTPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":770,\"y\":460,\"wires\":",[1107,6035],{},[441,6037],{"videoid":5648,"params":464,"style":4625,"title":3470},[31,6039,1425],{"id":1424},[15,6041,6042],{},"In this tutorial, you’ve learned how to connect your Arduino to Node-RED and control it using the Firmata protocol. We started by turning an LED on and off, reading sensor data, and building a flow to automate actions based on input from an IR sensor.",[15,6044,6045],{},"This approach is so powerful that you don’t need to write any code; FlowFuse lets you create automation flows with just a few clicks. You can quickly expand this setup to include more sensors, devices, and dashboards to build your IoT projects.",[15,6047,6048],{},"It’s a simple yet powerful way to interact with your Arduino, with endless possibilities. Enjoy exploring and building!",{"title":103,"searchDepth":104,"depth":104,"links":6050},[6051,6052,6060],{"id":819,"depth":107,"text":820},{"id":5670,"depth":107,"text":5671,"children":6053},[6054,6055,6056,6057,6058,6059],{"id":5677,"depth":294,"text":5678},{"id":5698,"depth":294,"text":5699},{"id":5743,"depth":294,"text":5744},{"id":5806,"depth":294,"text":5807},{"id":5898,"depth":294,"text":5899},{"id":5959,"depth":294,"text":5960},{"id":1424,"depth":107,"text":1425},"2025-02-12","Learn how to set up and control your Arduino remotely using Node-RED and FlowFuse. Explore the simplicity of automation flows","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Farduino-with-node-red.png","2025-07-23",{"keywords":6066,"excerpt":6067},"Arduino, Node-RED, Firmata, Node-RED IoT, automation, FlowFuse, LED control with Node-RED, IR sensor, input-output, serial communication, microcontroller, Arduino Uno, remote control, object detection, dashboard.",{"type":12,"value":6068},[6069],[15,6070,5642],{},"\u002Fblog\u002F2025\u002F02\u002Finteracting-with-arduino-using-node-red",{"title":5636,"description":6062},{"loc":6071},"blog\u002F2025\u002F02\u002Finteracting-with-arduino-using-node-red","Control and Automate Arduino with Node-RED",[138],"Arduino boards lack built-in internet connectivity, but using the Firmata protocol combined with Node-RED and FlowFuse enables remote control and automation of Arduino I\u002FO pins without writing any Arduino code. This tutorial walks through uploading the StandardFirmata sketch to Arduino, installing the node-red-contrib-arduino nodes in FlowFuse, and building flows to control an LED and read sensors.","JCWwYD5C_dz9ApX9ECSvIz42l_aMdAH5-7LZLz-OoIE",{"id":6080,"title":6081,"authors":6082,"body":6083,"cta":6197,"date":6200,"description":6201,"extension":116,"image":6202,"lastUpdated":6203,"meta":6204,"navigation":129,"path":6213,"seo":6214,"sitemap":6215,"stem":6216,"subtitle":6201,"tags":6217,"tldr":6219,"video":3,"__hash__":6220},"blog\u002Fblog\u002F2025\u002F02\u002Fnode-red-academy-announcement.md","Announcing Node-RED Academy!",[3030],{"type":12,"value":6084,"toc":6190},[6085,6092,6096,6099,6103,6106,6109,6113,6125,6133,6136,6159,6162,6166,6169,6172,6176,6182],[15,6086,175,6087,6091],{},[63,6088,660],{"href":6089,"rel":6090},"https:\u002F\u002Fnode-red-academy.learnworlds.com\u002F",[67]," provides Node-RED courses both for experts, who want to grow their knowledge, and for beginners, who are just learning the ropes. Get formal certification in Node-RED by completing courses and share certificates on LinkedIn to demonstrate your Node-RED learning accomplishments.",[31,6093,6095],{"id":6094},"what-is-node-red-academy","What is Node-RED Academy?",[15,6097,6098],{},"Node-RED Academy is a brand new learning portal for Node-RED. Whether you're a complete beginner to Node-RED or have been building applications for years, Node-RED Academy courses will take your expertise to the next level, and provide you with formal certification that are evidence of your Node-RED knowledge and skills.",[31,6100,6102],{"id":6101},"can-i-earn-a-credential","Can I Earn a Credential?",[15,6104,6105],{},"All courses completed through Node-RED Academy provide a formal certification upon successful course completion. You can share this certificate on LinkedIn, add it to your CV or show it off anywhere else you like.",[15,6107,6108],{},"The credential will be evidence of your Node-RED knowledge and abilities, and will be a great way to demonstrate your expertise to potential employers or clients.",[31,6110,6112],{"id":6111},"what-courses-are-included","What Courses are Included?",[15,6114,6115,6116,6120,6121,29],{},"Our first course, The ",[63,6117,6119],{"href":6089,"rel":6118},[67],"Node-RED Fundamentals",", takes about 90 minutes to complete, is free and available now. ",[63,6122,6124],{"href":6089,"rel":6123},[67],"Check it out!",[15,6126,6127,6131],{},[165,6128],{"alt":6129,"dataZoomable":103,"src":6130},"Screenshot of the Node-RED Fundamentals course in progress","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Facademy-screenshot.png",[170,6132,6129],{},[15,6134,6135],{},"We also have plans for the following courses to follow in the future:",[39,6137,6138,6141,6144,6147,6150,6153,6156],{},[42,6139,6140],{},"Node-RED Advanced",[42,6142,6143],{},"Building Applications with Node-RED",[42,6145,6146],{},"Node-RED for Industry",[42,6148,6149],{},"Node-RED for Teams",[42,6151,6152],{},"FlowFuse Fundamentals",[42,6154,6155],{},"FlowFuse for Teams",[42,6157,6158],{},"FlowFuse Advanced",[15,6160,6161],{},"This list will likely grow beyond this too. We will be evolving our courses based on feedback and ideas from the community too!",[31,6163,6165],{"id":6164},"whos-behind-the-node-red-academy","Who's behind the Node-RED Academy?",[15,6167,6168],{},"There are many great resources for Node-RED education out there, but the Node-RED Academy has been curated by the authors and developers behind Node-RED and covers everything you'll need to know from building your first flow to integrating with industrial hardware.",[15,6170,6171],{},"FlowFuse is an enterprise-grade industrial data platform that enables engineers to build, manage, scale, and secure their Node-RED solutions for digitalizing processes and operations. You can sign up for free [here]({% include \"sign-up-url.njk\" %}) to give it a go.",[31,6173,6175],{"id":6174},"how-do-i-get-started","How Do I Get Started?",[15,6177,4574,6178,6181],{},[63,6179,660],{"href":6089,"rel":6180},[67]," and sign up for free.",[15,6183,6184,6188],{},[165,6185],{"alt":6186,"dataZoomable":103,"src":6187},"Screenshot of the Node-RED Academy home page","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Facademy-home-page.png",[170,6189,6186],{},{"title":103,"searchDepth":104,"depth":104,"links":6191},[6192,6193,6194,6195,6196],{"id":6094,"depth":107,"text":6095},{"id":6101,"depth":107,"text":6102},{"id":6111,"depth":107,"text":6112},{"id":6164,"depth":107,"text":6165},{"id":6174,"depth":107,"text":6175},{"type":748,"title":6198,"description":6199},"Learn Node-RED, Then Put It Into Production","FlowFuse gives you the enterprise platform to deploy, manage, and scale everything you build, from your first flow to factory-floor production systems.","2025-02-05","The new Node-RED Academy enables you to learn Node-RED, increase your expertise, and demonstrate your knowledge with shareable certificates.","\u002Fblog\u002F2025\u002F02\u002Fimages\u002Facademy-announcement.png","2026-06-03",{"keywords":6205,"excerpt":6206},"node-red academy, node-red courses, node-red certification, learn node-red, node-red training",{"type":12,"value":6207},[6208],[15,6209,175,6210,6091],{},[63,6211,660],{"href":6089,"rel":6212},[67],"\u002Fblog\u002F2025\u002F02\u002Fnode-red-academy-announcement",{"title":6081,"description":6201},{"loc":6213},"blog\u002F2025\u002F02\u002Fnode-red-academy-announcement",[10,138,6218,660,136],"education","Node-RED Academy is a new free learning portal where developers can take structured Node-RED courses, earn shareable certificates, and grow from beginner to expert. The first course, Node-RED Fundamentals, is free, takes roughly 90 minutes, and is available now, with more courses covering advanced topics and FlowFuse planned for the future.","hCm2ZUdmu3pOj4ncFNX3GhRReZLJg4Y9bty6LBAqU6g",{"id":6222,"title":6223,"authors":6224,"body":6226,"cta":7439,"date":7442,"description":7443,"extension":116,"image":7444,"lastUpdated":6203,"meta":7445,"navigation":129,"path":7451,"seo":7452,"sitemap":7453,"stem":7454,"subtitle":7455,"tags":7456,"tldr":7458,"video":3,"__hash__":7459},"blog\u002Fblog\u002F2025\u002F01\u002Fintegrating-siemens-s7-plcs-with-node-red-guide.md","How to Read and Write Siemens S7 PLC Data, A Node-RED Guide (2026)",[144,6225],"stephen-mclaughlin",{"type":12,"value":6227,"toc":7425},[6228,6231,6234,6237,6241,6244,6249,6254,6262,6267,6275,6280,6289,6297,6302,6307,6311,6314,6317,6325,6328,6332,6344,6348,6365,6368,6372,6375,7065,7068,7077,7081,7084,7098,7106,7114,7122,7130,7138,7142,7145,7158,7166,7178,7181,7184,7190,7194,7197,7204,7207,7216,7219,7222,7225,7254,7262,7268,7271,7274,7277,7280,7289,7326,7335,7396,7400,7403,7411,7414,7416,7419],[15,6229,6230],{},"Siemens S7 PLCs are a staple in industrial automation, powering everything from basic control functions to complex, large-scale processes. However, integrating these PLCs with other systems for remote monitoring or data sharing can present challenges.",[15,6232,6233],{},"This is where Node-RED comes in, offering a user-friendly solution to seamlessly connect Siemens S7 PLCs with a variety of platforms. With its intuitive flow-based interface, Node-RED enables you to create custom workflows and dashboards, no deep technical expertise required.",[15,6235,6236],{},"Siemens S7 PLCs are typically programmed using TIA Portal, Siemens' integrated development environment, and communication with external systems usually relies on the S7 protocol (ISO over TCP\u002FIP). In this article, we’ll walk you through how to use Node-RED to read from and write to Siemens S7 PLCs via the S7 protocol, unlocking new possibilities for remote control and system integration in your industrial automation setup.",[31,6238,6240],{"id":6239},"prerequisite","Prerequisite",[15,6242,6243],{},"Before integrating your Siemens S7 PLC with Node-RED, make sure you have the following :",[271,6245,6246],{},[42,6247,6248],{},"Before downloading the ladder program and all configurations and settings to your PLC, make sure you have the following settings:",[39,6250,6251],{},[42,6252,6253],{},"Allow PUT\u002FGET Communication from remote partners.",[15,6255,6256,6260],{},[165,6257],{"alt":6258,"src":6259,"dataZoomable":103},"PUT\u002FGET Communication from remote partners is Allowed","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fallow-put-get-communication.png",[170,6261,6258],{},[39,6263,6264],{},[42,6265,6266],{},"Provide full access to the PLC (no protection), allowing unrestricted access to data exchange.",[15,6268,6269,6273],{},[165,6270],{"alt":6271,"src":6272,"dataZoomable":103},"Providing complete access to the PLC","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fproviding-full-access-to-plc.png",[170,6274,6271],{},[271,6276,6277],{"start":107},[42,6278,6279],{},"Ensure that the appropriate ladder program (or any other logic) is written according to your requirements and successfully downloaded to the PLC. However, before downloading, make sure the 'Optimized Block Access' option is disabled for the data block that your ladder program using.",[15,6281,6282,6286],{},[165,6283],{"alt":6284,"src":6285,"dataZoomable":103},"Untick 'Optimized Block Access'.","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Foptimized-block-access.png",[170,6287,6288],{},"Untick 'Optimized Block Access.'",[271,6290,6291],{"start":294},[42,6292,6293,6294,6296],{},"Install Node-RED on the device that will communicate with the S7 PLC. You cannot install Node-RED directly on the S7 PLC, as PLCs are typically controllers, not computers. For example, you can use a device like the Revolutionary Pi to connect and transfer data across systems. Use the ",[63,6295,3179],{"href":4664}," to install Node-RED on your device.",[39,6298,6299],{},[42,6300,6301],{},"Why FlowFuse Device Agent? It allows you to manage Node-RED remotely, enabling control, monitoring, and flow creation without the need for on-site visits. FlowFuse also offers a suite of enterprise-grade features such as collaboration, device management, and DevOps pipelines, which are essential in industrial environments. These features help streamline operations and ensure scalability in complex automation systems. [Sign up for free]({% include \"sign-up-url.njk\" %}) to get started.",[271,6303,6304],{"start":104},[42,6305,6306],{},"Verify that the device running Node-RED is in the same network as the PLC and can successfully ping the PLC. Also, a firewall should not block the S7 port (typically port 102).",[31,6308,6310],{"id":6309},"integrating-siemens-s7-plcs-with-node-red","Integrating Siemens S7 PLCs with Node-RED",[15,6312,6313],{},"Now that everything is set up, let's integrate your Siemens S7 PLC with Node-RED. In this article, I’ll demonstrate the process using a Siemens S7-1212C PLC. I’ve connected it to a stack\u002Ftower light and will walk you through how to write data to the PLC to control this light. Later, I’ll show you how to read data and reflect the status of the light.",[15,6315,6316],{},"My program in TIA Portal is structured as shown below, utilizing DB (Data Blocks) and Q (physical outputs) to control devices. However, Node-RED can retrieve almost all types of data from the PLC. The process is similar for most data types.",[15,6318,6319,6323],{},[165,6320],{"alt":6321,"src":6322},"Ladder Logic to Control Outputs for Managing Lights","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fladder-to-control-lights.png",[170,6324,6321],{},[15,6326,6327],{},"Let’s break down what’s happening in the ladder logic above. First, we have open contacts, each with address variables defined in a separate Data Block. There are three open branches, each starting with an open contact. Each contact is connected to an output that alters the status of a Q physical address. Each Q corresponds to a physical output on the PLC, which is wired to the lights. When we change the status of a contact to \"true,\" it activates the corresponding light by altering the state of the Q output, which reflects the change in the physical output.",[399,6329,6331],{"id":6330},"installing-the-s7-node","Installing the S7 Node",[15,6333,6334,6335,6338,6339,29],{},"To communicate from Node-RED to the PLC, we need to install the S7 node, which allows Node-RED to interface with Siemens S7 PLCs. In this article, we will be using ",[924,6336,6337],{},"node-red-contrib-s7",", which is quite popular. If this particular node is not suitable for your workflow you can find alternatives in the ",[63,6340,6343],{"href":6341,"rel":6342},"https:\u002F\u002Fflows.nodered.org\u002Fsearch?term=siemens&type=node",[67],"Node-RED catalog",[1165,6345,6347],{"id":6346},"steps-to-install-the-s7-node","Steps to Install the S7 Node:",[271,6349,6350,6353,6355,6357,6363],{},[42,6351,6352],{},"Open your Node-RED editor in a web browser.",[42,6354,5760],{},[42,6356,5763],{},[42,6358,6359,6360,6362],{},"Switch to the \"Install\" tab and type ",[924,6361,6337],{}," in the search field.",[42,6364,3289],{},[15,6366,6367],{},"Once the installation is complete, the S7 nodes will be available in your Node-RED palette, and you can start using it to communicate with your Siemens S7 PLC.",[399,6369,6371],{"id":6370},"addressing-scheme-for-variables-in-node-red-with-the-s7-node","Addressing Scheme for Variables in Node-RED with the S7 Node",[15,6373,6374],{},"Before we start, it's important to note that the variables and their addresses configured on the S7 endpoint follow a slightly different addressing scheme compared to those used in Step 7 or the TIA Portal. Therefore, when adding variables to the S7 node in Node-RED, you must ensure that you follow the correct addressing format outlined in the table below.",[6376,6377,6378,6405],"table",{},[6379,6380,6381],"thead",{},[6382,6383,6384,6390,6395,6400],"tr",{},[6385,6386,6387],"th",{},[19,6388,6389],{},"Node-RED Address",[6385,6391,6392],{},[19,6393,6394],{},"Step7 Equivalent",[6385,6396,6397],{},[19,6398,6399],{},"Data Type",[6385,6401,6402],{},[19,6403,6404],{},"Description",[6406,6407,6408,6427,6445,6463,6481,6498,6516,6533,6551,6566,6582,6598,6614,6630,6646,6662,6679,6696,6713,6730,6747,6764,6780,6796,6812,6829,6846,6863,6879,6895,6911,6928,6944,6960,6975,6989,7003,7017,7033,7049],"tbody",{},[6382,6409,6410,6416,6421,6424],{},[6411,6412,6413],"td",{},[924,6414,6415],{},"DB5,X0.1",[6411,6417,6418],{},[924,6419,6420],{},"DB5.DBX0.1",[6411,6422,6423],{},"Boolean",[6411,6425,6426],{},"Bit 1 of byte 0 in DB5",[6382,6428,6429,6434,6439,6442],{},[6411,6430,6431],{},[924,6432,6433],{},"DB23,BYTE1",[6411,6435,6436],{},[924,6437,6438],{},"DB23.DBB1",[6411,6440,6441],{},"Number (Byte)",[6411,6443,6444],{},"Byte 1 (0-255) of DB23",[6382,6446,6447,6452,6457,6460],{},[6411,6448,6449],{},[924,6450,6451],{},"DB100,CHAR2",[6411,6453,6454],{},[924,6455,6456],{},"DB100.DBB2",[6411,6458,6459],{},"String",[6411,6461,6462],{},"Byte 2 of DB100 as Char",[6382,6464,6465,6470,6475,6478],{},[6411,6466,6467],{},[924,6468,6469],{},"DB42,INT3",[6411,6471,6472],{},[924,6473,6474],{},"DB42.DBW3",[6411,6476,6477],{},"Number (16-bit)",[6411,6479,6480],{},"Signed 16-bit number at byte 3 in DB42",[6382,6482,6483,6488,6493,6495],{},[6411,6484,6485],{},[924,6486,6487],{},"DB57,WORD4",[6411,6489,6490],{},[924,6491,6492],{},"DB57.DBW4",[6411,6494,6477],{},[6411,6496,6497],{},"Unsigned 16-bit number at byte 4 in DB57",[6382,6499,6500,6505,6510,6513],{},[6411,6501,6502],{},[924,6503,6504],{},"DB13,DINT5",[6411,6506,6507],{},[924,6508,6509],{},"DB13.DBD5",[6411,6511,6512],{},"Number (32-bit)",[6411,6514,6515],{},"Signed 32-bit number at byte 5 in DB13",[6382,6517,6518,6523,6528,6530],{},[6411,6519,6520],{},[924,6521,6522],{},"DB19,DWORD6",[6411,6524,6525],{},[924,6526,6527],{},"DB19.DBD6",[6411,6529,6512],{},[6411,6531,6532],{},"Unsigned 32-bit number at byte 6 in DB19",[6382,6534,6535,6540,6545,6548],{},[6411,6536,6537],{},[924,6538,6539],{},"DB21,REAL7",[6411,6541,6542],{},[924,6543,6544],{},"DB21.DBD7",[6411,6546,6547],{},"Floating Point (32)",[6411,6549,6550],{},"Floating point number at byte 7 in DB21",[6382,6552,6553,6558,6561,6563],{},[6411,6554,6555],{},[924,6556,6557],{},"DB2,S7.10*",[6411,6559,6560],{},"-",[6411,6562,6459],{},[6411,6564,6565],{},"String (length 10) starting at byte 7 in DB2",[6382,6567,6568,6573,6577,6579],{},[6411,6569,6570],{},[924,6571,6572],{},"I1.0",[6411,6574,6575],{},[924,6576,6572],{},[6411,6578,6423],{},[6411,6580,6581],{},"Bit 0 of byte 1 in input area",[6382,6583,6584,6589,6593,6595],{},[6411,6585,6586],{},[924,6587,6588],{},"Q2.1",[6411,6590,6591],{},[924,6592,6588],{},[6411,6594,6423],{},[6411,6596,6597],{},"Bit 1 of byte 2 in output area",[6382,6599,6600,6605,6609,6611],{},[6411,6601,6602],{},[924,6603,6604],{},"M3.2",[6411,6606,6607],{},[924,6608,6604],{},[6411,6610,6423],{},[6411,6612,6613],{},"Bit 2 of byte 3 in memory area",[6382,6615,6616,6621,6625,6627],{},[6411,6617,6618],{},[924,6619,6620],{},"IB4",[6411,6622,6623],{},[924,6624,6620],{},[6411,6626,6441],{},[6411,6628,6629],{},"Byte 4 (0-255) in input area",[6382,6631,6632,6637,6641,6643],{},[6411,6633,6634],{},[924,6635,6636],{},"QB5",[6411,6638,6639],{},[924,6640,6636],{},[6411,6642,6441],{},[6411,6644,6645],{},"Byte 5 (0-255) in output area",[6382,6647,6648,6653,6657,6659],{},[6411,6649,6650],{},[924,6651,6652],{},"MB6",[6411,6654,6655],{},[924,6656,6652],{},[6411,6658,6441],{},[6411,6660,6661],{},"Byte 6 (0-255) in memory area",[6382,6663,6664,6669,6674,6676],{},[6411,6665,6666],{},[924,6667,6668],{},"IC7",[6411,6670,6671],{},[924,6672,6673],{},"IB7",[6411,6675,6459],{},[6411,6677,6678],{},"Byte 7 of input area as Char",[6382,6680,6681,6686,6691,6693],{},[6411,6682,6683],{},[924,6684,6685],{},"QC8",[6411,6687,6688],{},[924,6689,6690],{},"QB8",[6411,6692,6459],{},[6411,6694,6695],{},"Byte 8 of output area as Char",[6382,6697,6698,6703,6708,6710],{},[6411,6699,6700],{},[924,6701,6702],{},"MC9",[6411,6704,6705],{},[924,6706,6707],{},"MB9",[6411,6709,6459],{},[6411,6711,6712],{},"Byte 9 of memory area as Char",[6382,6714,6715,6720,6725,6727],{},[6411,6716,6717],{},[924,6718,6719],{},"II10",[6411,6721,6722],{},[924,6723,6724],{},"IW10",[6411,6726,6477],{},[6411,6728,6729],{},"Signed 16-bit number at byte 10 in input area",[6382,6731,6732,6737,6742,6744],{},[6411,6733,6734],{},[924,6735,6736],{},"QI12",[6411,6738,6739],{},[924,6740,6741],{},"QW12",[6411,6743,6477],{},[6411,6745,6746],{},"Signed 16-bit number at byte 12 in output area",[6382,6748,6749,6754,6759,6761],{},[6411,6750,6751],{},[924,6752,6753],{},"MI14",[6411,6755,6756],{},[924,6757,6758],{},"MW14",[6411,6760,6477],{},[6411,6762,6763],{},"Signed 16-bit number at byte 14 in memory area",[6382,6765,6766,6771,6775,6777],{},[6411,6767,6768],{},[924,6769,6770],{},"IW16",[6411,6772,6773],{},[924,6774,6770],{},[6411,6776,6477],{},[6411,6778,6779],{},"Unsigned 16-bit number at byte 16 in input area",[6382,6781,6782,6787,6791,6793],{},[6411,6783,6784],{},[924,6785,6786],{},"QW18",[6411,6788,6789],{},[924,6790,6786],{},[6411,6792,6477],{},[6411,6794,6795],{},"Unsigned 16-bit number at byte 18 in output area",[6382,6797,6798,6803,6807,6809],{},[6411,6799,6800],{},[924,6801,6802],{},"MW20",[6411,6804,6805],{},[924,6806,6802],{},[6411,6808,6477],{},[6411,6810,6811],{},"Unsigned 16-bit number at byte 20 in memory area",[6382,6813,6814,6819,6824,6826],{},[6411,6815,6816],{},[924,6817,6818],{},"IDI22",[6411,6820,6821],{},[924,6822,6823],{},"ID22",[6411,6825,6512],{},[6411,6827,6828],{},"Signed 32-bit number at byte 22 in input area",[6382,6830,6831,6836,6841,6843],{},[6411,6832,6833],{},[924,6834,6835],{},"QDI24",[6411,6837,6838],{},[924,6839,6840],{},"QD24",[6411,6842,6512],{},[6411,6844,6845],{},"Signed 32-bit number at byte 24 in output area",[6382,6847,6848,6853,6858,6860],{},[6411,6849,6850],{},[924,6851,6852],{},"MDI26",[6411,6854,6855],{},[924,6856,6857],{},"MD26",[6411,6859,6512],{},[6411,6861,6862],{},"Signed 32-bit number at byte 26 in memory area",[6382,6864,6865,6870,6874,6876],{},[6411,6866,6867],{},[924,6868,6869],{},"ID28",[6411,6871,6872],{},[924,6873,6869],{},[6411,6875,6512],{},[6411,6877,6878],{},"Unsigned 32-bit number at byte 28 in input area",[6382,6880,6881,6886,6890,6892],{},[6411,6882,6883],{},[924,6884,6885],{},"QD30",[6411,6887,6888],{},[924,6889,6885],{},[6411,6891,6512],{},[6411,6893,6894],{},"Unsigned 32-bit number at byte 30 in output area",[6382,6896,6897,6902,6906,6908],{},[6411,6898,6899],{},[924,6900,6901],{},"MD32",[6411,6903,6904],{},[924,6905,6901],{},[6411,6907,6512],{},[6411,6909,6910],{},"Unsigned 32-bit number at byte 32 in memory area",[6382,6912,6913,6918,6922,6925],{},[6411,6914,6915],{},[924,6916,6917],{},"IR34",[6411,6919,6920],{},[924,6921,6917],{},[6411,6923,6924],{},"Floating Point",[6411,6926,6927],{},"Floating point number at byte 34 in input area",[6382,6929,6930,6935,6939,6941],{},[6411,6931,6932],{},[924,6933,6934],{},"QR36",[6411,6936,6937],{},[924,6938,6934],{},[6411,6940,6924],{},[6411,6942,6943],{},"Floating point number at byte 36 in output area",[6382,6945,6946,6951,6955,6957],{},[6411,6947,6948],{},[924,6949,6950],{},"MR38",[6411,6952,6953],{},[924,6954,6950],{},[6411,6956,6924],{},[6411,6958,6959],{},"Floating point number at byte 38 in memory area",[6382,6961,6962,6967,6969,6972],{},[6411,6963,6964],{},[924,6965,6966],{},"DB1,DT0",[6411,6968,6560],{},[6411,6970,6971],{},"Date",[6411,6973,6974],{},"Timestamp in DATE_AND_TIME format",[6382,6976,6977,6982,6984,6986],{},[6411,6978,6979],{},[924,6980,6981],{},"DB1,DTZ10",[6411,6983,6560],{},[6411,6985,6971],{},[6411,6987,6988],{},"Timestamp in DATE_AND_TIME format (UTC)",[6382,6990,6991,6996,6998,7000],{},[6411,6992,6993],{},[924,6994,6995],{},"DB2,DTL2",[6411,6997,6560],{},[6411,6999,6971],{},[6411,7001,7002],{},"Timestamp in DTL format",[6382,7004,7005,7010,7012,7014],{},[6411,7006,7007],{},[924,7008,7009],{},"DB2,DTLZ12",[6411,7011,6560],{},[6411,7013,6971],{},[6411,7015,7016],{},"Timestamp in DTL format (UTC)",[6382,7018,7019,7024,7028,7030],{},[6411,7020,7021],{},[924,7022,7023],{},"DB57,RWORD4",[6411,7025,7026],{},[924,7027,6492],{},[6411,7029,6477],{},[6411,7031,7032],{},"Unsigned 16-bit number, Little-Endian at byte 4",[6382,7034,7035,7040,7044,7046],{},[6411,7036,7037],{},[924,7038,7039],{},"DB13,RDI5",[6411,7041,7042],{},[924,7043,6509],{},[6411,7045,6512],{},[6411,7047,7048],{},"Signed 32-bit number, Little-Endian at byte 5",[6382,7050,7051,7056,7060,7062],{},[6411,7052,7053],{},[924,7054,7055],{},"MRW20",[6411,7057,7058],{},[924,7059,6802],{},[6411,7061,6477],{},[6411,7063,7064],{},"Unsigned 16-bit number, Little-Endian at byte 20",[15,7066,7067],{},"For example, consider that you have a ladder logic program in the TIA Portal with addresses like DB5.DBX0.0 and DB13.DBW4. You must adjust the address format slightly when you want to use these in the Node-RED S7 node. In Node-RED, DB5.DBX0.0 would be represented as DB5,X0.0 and DB13.DBW4 would be written as DB13,WORD4. Essentially, you look at the TIA Portal address, find the corresponding format in the Node-RED address column, and use that format in the S7 node configuration.",[15,7069,7070,7071,7076],{},"If you wanted integrate Siemens LOGO, please refer to the node's ",[63,7072,7075],{"href":7073,"rel":7074},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-s7",[67],"README",", as the addressing differs.",[399,7078,7080],{"id":7079},"configuring-the-s7-node-to-connect-to-the-plc","Configuring the S7 Node to Connect to the PLC",[15,7082,7083],{},"Now that you have all the necessary knowledge and setup, let's start by establishing a connection between Node-RED and your Siemens S7 PLC. The S7 node in Node-RED simplifies the process, making it easy to configure communication. Follow the steps below to connect and start interacting with your PLC",[271,7085,7086,7089,7092,7095],{},[42,7087,7088],{},"Drag the S7 node onto the Node-RED canvas.",[42,7090,7091],{},"Double-click on the S7 node and click on the \"+\" icon to add a PLC configuration.",[42,7093,7094],{},"Select \"Ethernet (ISO on TCP)\" as the transport protocol, then enter your PLC's IP address. The default port (102) is used for S7 communication, so leave it unchanged.",[42,7096,7097],{},"Set the Mode to \"Rack,\" then enter the Rack ID and Slot ID. These values can be found in the TIA Portal under the Device View tab on your configured device.",[15,7099,7100,7104],{},[165,7101],{"alt":7102,"src":7103,"dataZoomable":103},"Image showing window from where you will get the Rack No and Slot No","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fshowing-rack-and-slot.png",[170,7105,7102],{},[271,7107,7108,7111],{"start":1868},[42,7109,7110],{},"Enter the Cycle Time (interval for communication with the PLC) and Timeout Duration (maximum time to wait for a response).",[42,7112,7113],{},"Once done, switch to the Variables tab and add all the variables with the correct address and name you want to read or write.",[15,7115,7116,7120],{},[165,7117],{"alt":7118,"src":7119,"dataZoomable":103},"Adding Variables into s7 node","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fs7-config-variables.png",[170,7121,7118],{},[271,7123,7124,7127],{"start":1623},[42,7125,7126],{},"After adding the variables, click Add and then Done.",[42,7128,7129],{},"Deploy the flow by clicking the top-right Deploy button. Once deployed, the connection status will be displayed at the bottom of the node. If connected successfully, it will show a green squre with \"online\" status.",[15,7131,7132,7136],{},[165,7133],{"alt":7134,"src":7135,"dataZoomable":103},"Configuring S7 node for connection","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fs7-connection-configuration.png",[170,7137,7134],{},[399,7139,7141],{"id":7140},"writing-data-to-the-plc","Writing Data to the PLC",[15,7143,7144],{},"Now that you’ve configured the connection, it’s time to use Node-RED to write data to the PLC to control light.",[271,7146,7147,7150,7153,7156],{},[42,7148,7149],{},"Drag the s7-out node onto the canvas.",[42,7151,7152],{},"Double-click on the node and select the variable to which you want to update or write a value.",[42,7154,7155],{},"Select the PLC configuration that we have added.",[42,7157,5195],{},[15,7159,7160,7164],{},[165,7161],{"alt":7162,"src":7163,"dataZoomable":103},"Configuring S7-out Node to write data to plc","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fconfiguring-s7-out-node.png",[170,7165,7162],{},[271,7167,7168,7175],{"start":1868},[42,7169,7170,7171,7174],{},"The node is now ready to write data to the PLC. You can use standard Node-RED nodes like Inject, Change, or Function to create a workflow that sends the data. Ensure the data type matches the configuration set in the PLC program. For example, in my ladder logic, I need to modify the status of individual open contacts, each with its own address, such as DB1.DBX0.0, DB1.DBX0.1, and DB1.DBX0.2, to control the tower lights. Setting these contacts to TRUE will turn on the red, yellow, and green lights, respectively. You can send the data using the nodes I’ve mentioned, or you can build a custom dashboard with ",[63,7172,3465],{"href":7173},"\u002Fplatform\u002Fdashboard\u002F"," for easier interaction.",[42,7176,7177],{},"Once your flow is set up and the s7-out node for each variable is configured, click Deploy in the top-right corner to activate the flow.",[441,7179],{"videoid":7180,"params":464,"style":4625,"title":3470},"AilWMNPzP1Q",[15,7182,7183],{},"In the video above, the dashboard interface is built to control the stack light. At the end of this article, I will provide the complete flow for you to download.",[15,7185,7186,7187,29],{},"If you're building a dashboard, keep in mind that while you can create it on Node-RED within the remote instance on FlowFuse Device Agent, you won’t be able to access it remotely across the editor tunnel. You can of course access it locally on the device or on the local LAN. For this demonstration, I wish to access the dashboard remotely across the internet and so I will create the dashboard in a hosted instance of Node-RED and use the FlowFuse Projects nodes to simply and securely pass the necessary values to and from the remote Node-RED instance. For more details on how to set this up, check out our article: ",[63,7188,7189],{"href":4907},"Exploring FlowFuse Project Nodes",[399,7191,7193],{"id":7192},"reading-data-from-the-plc","Reading Data from the PLC",[15,7195,7196],{},"Now that we’ve covered how to write data to your Siemens S7 PLC, let's move on to reading data from it. Node-RED makes it easy to retrieve important information such as the status of inputs, outputs, or internal memory. By pulling this data into your workflows or visualizing it on a dashboard, you can monitor key parameters in real time and gain valuable insights.",[15,7198,7199,7200,29],{},"However, before we dive in, it's important to consider that reading individual data points one by one in large-scale manufacturing systems can lead to delays. This approach may not be efficient, especially when dealing with a large number of data points. For more information on these challenges and potential solutions, you can refer to this article: ",[63,7201,7203],{"href":7202},"\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data-part2\u002F","Modernize Your Legacy Industrial Data - Part 2",[15,7205,7206],{},"To address this issue, you can optimize data retrieval by storing output status values in a single word or double word within the PLC. For our example, I have created a custom function in my program that assigns the output values to individual bits of the word.",[15,7208,7209,7213],{},[165,7210],{"alt":7211,"src":7212,"dataZoomable":103},"Ladder diagram showing a custom function that stores the status of outputs in a single word within the PLC.","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fcustom-function-storing-bits-in-word.png",[170,7214,7215],{},"Custom ladder diagram function storing output statuses in a single word for optimized data retrieval.",[15,7217,7218],{},"There are several ways to implement this, and depending on your system’s needs, some methods may be more efficient than others. In this case, the output values are stored in a single word within the PLC, as shown in the ladder diagram above. This is not the only correct method, it's simply one approach that works for this particular scenario. Feel free to adapt or explore other methods that might better suit your setup.",[15,7220,7221],{},"Additionally, if the data you’re reading is mission-critical and you can't afford to lose any, consider using a FIFO stack or buffer in your PLC program. This method ensures that even if there is a network outage or computer problem, no data is lost as it will remain siting in the stack until your Node-RED is back on line and retrieves it.  This ensures no gaps or interruptions in your data and guarantees data integrity.",[15,7223,7224],{},"Now, let’s begin reading the data from the PLC.",[271,7226,7227,7232,7235,7238,7248,7251],{},[42,7228,5054,7229,1216],{},[924,7230,7231],{},"s7-in",[42,7233,7234],{},"Double-click on the node to open the configuration and select the appropriate PLC configuration from the list of available connections.",[42,7236,7237],{},"Choose the appropriate mode based on your requirements. If you want to read only one variable, select \"Single Variable Mode\". In this mode, the \"Variable\" dropdown will allow you to select only a single variable at a time. If you need to read multiple variables, you can select \"All Variables\" mode, but be aware that the node might still process each request sequentially, depending on its internal workings (which is not fully documented). This can be inefficient when dealing with hundreds of variables.",[42,7239,7240,7241,7244,7245,29],{},"Choose the variable that corresponds to the word or double word containing all the data points you want to read. For example, if you’ve configured the word in the PLC as ",[924,7242,7243],{},"DB.DBW2",", the format in the s7-in node will be ",[924,7246,7247],{},"DB,WORD2",[42,7249,7250],{},"Enable the \"Emit only when value changes (diff)\" option to ensure that the node only triggers when the value of the variable changes, reducing unnecessary reads and improving efficiency.",[42,7252,7253],{},"Once your configuration is set, click \"Done\" and then deploy the flow to start reading data from the PLC.",[15,7255,7256,7260],{},[165,7257],{"alt":7258,"src":7259,"dataZoomable":103},"Configuring S7-in Node to Read data from plc","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fconfiguring-s7-in-node.png",[170,7261,7258],{},[15,7263,7264,7265,7267],{},"You can add a \"Debug\" node to the ",[924,7266,7231],{}," node's output to verify that the data is being read correctly.",[15,7269,7270],{},"Once you see the printed data, you might be surprised, or perhaps you already expected this: since the word data type we're reading is not directly available in Node.js or Node-RED, we'll receive it as an integer. But don't worry, you can convert it into the format that suits your needs using node-red-contrib-buffer-parser. In this integer scenario, you'll need to shift the bits or extract individual values to match your desired output, such as isolating specific bits to represent different statuses or control points. The flow provided at the end demonstrates the implementation of this conversion.",[15,7272,7273],{},"Now that you have the desired format for your output data, you may want to build a dashboard interface with LEDs, gauges, or charts to monitor and visualize the data you've retrieved. You can use the FlowFuse Dashboard, as suggested earlier.",[15,7275,7276],{},"The video below shows the updated dashboard interface used to monitor the stack light LED status:",[441,7278],{"videoid":7279,"params":464,"style":4625,"title":3470},"Nlyk_BATKGE",[15,7281,7282,7283,1413,7285,7288],{},"Here is the flow you can import into your FlowFuse remote instance and deploy. Ensure that you have installed ",[924,7284,6337],{},[924,7286,7287],{},"node-red-contrib-buffer-parser",". This flow includes S7 nodes for interacting with the S7 PLC and Project nodes for communicating with the FlowFuse hosted instance, where you will build the dashboard.",[15,7290,4917,7291,1150],{},[1107,7292,7293,7294,7297,7298,7300,7301,7303,7304,7306,7307,7310,7311,7314,7315,7317,7318,7321,7322,7325],{},"{\"id\":\"0ffc8c2703b5e059\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,7295,7296],{},"\"061313277591a004\",\"a8499bc2443f0bd9\",\"2974dd47fda54b9c\",\"4c009f6076f47eb6\",\"f4378d1e7c268e1e\",\"63abd67743263739\"",",\"x\":54,\"y\":99,\"w\":732,\"h\":202},{\"id\":\"061313277591a004\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"endpoint\":\"f2f06ce027c97e4d\",\"variable\":\"Button_1\",\"name\":\"Button to Turn the RED Light ON\",\"x\":620,\"y\":140,\"wires\":",[1107,7299],{},"},{\"id\":\"a8499bc2443f0bd9\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"endpoint\":\"f2f06ce027c97e4d\",\"variable\":\"Button_2\",\"name\":\"Button to turn the Yellow light ON\",\"x\":620,\"y\":200,\"wires\":",[1107,7302],{},"},{\"id\":\"2974dd47fda54b9c\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"endpoint\":\"f2f06ce027c97e4d\",\"variable\":\"Button_3\",\"name\":\"Button to turn Green light  ON\",\"x\":610,\"y\":260,\"wires\":",[1107,7305],{},"},{\"id\":\"4c009f6076f47eb6\",\"type\":\"project link in\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"name\":\"Project in node to control the red light\",\"project\":\"all\",\"broadcast\":true,\"topic\":\"light_control_red\",\"x\":230,\"y\":140,\"wires\":[[\"061313277591a004\"]]},{\"id\":\"f4378d1e7c268e1e\",\"type\":\"project link in\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"name\":\"Project in node to control the yellow light\",\"project\":\"all\",\"broadcast\":true,\"topic\":\"light_control_yellow\",\"x\":240,\"y\":200,\"wires\":[[\"a8499bc2443f0bd9\"]]},{\"id\":\"63abd67743263739\",\"type\":\"project link in\",\"z\":\"FFF0000000000001\",\"g\":\"0ffc8c2703b5e059\",\"name\":\"Project in node to control the green light\",\"project\":\"all\",\"broadcast\":true,\"topic\":\"light_control_green\",\"x\":240,\"y\":260,\"wires\":[[\"2974dd47fda54b9c\"]]},{\"id\":\"f2f06ce027c97e4d\",\"type\":\"s7 endpoint\",\"transport\":\"iso-on-tcp\",\"address\":\"192.168.1.6\",\"port\":\"102\",\"rack\":\"0\",\"slot\":\"1\",\"localtsaphi\":\"01\",\"localtsaplo\":\"00\",\"remotetsaphi\":\"01\",\"remotetsaplo\":\"00\",\"connmode\":\"rack-slot\",\"adapter\":\"\",\"busaddr\":\"2\",\"cycletime\":\"1000\",\"timeout\":\"2000\",\"name\":\"S7 Connection Configuration\",\"vartable\":",[1107,7308,7309],{},"{\"addr\":\"DB1,X0.0\",\"name\":\"Button_1\"},{\"addr\":\"DB1,X0.1\",\"name\":\"Button_2\"},{\"addr\":\"DB1,X0.2\",\"name\":\"Button_3\"},{\"addr\":\"DB1,WORD2\",\"name\":\"LightStatus\"}","},{\"id\":\"23fd40630dbef712\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,7312,7313],{},"\"a45637418005d0e5\",\"a8ea1622d1fad4ba\",\"8d9a9dc4183a778e\",\"d60a74a5430df7ae\"",",\"x\":54,\"y\":339,\"w\":972,\"h\":82},{\"id\":\"a45637418005d0e5\",\"type\":\"s7 in\",\"z\":\"FFF0000000000001\",\"g\":\"23fd40630dbef712\",\"endpoint\":\"f2f06ce027c97e4d\",\"mode\":\"single\",\"variable\":\"LightStatus\",\"diff\":true,\"name\":\"\",\"x\":150,\"y\":380,\"wires\":[[\"d60a74a5430df7ae\"]]},{\"id\":\"a8ea1622d1fad4ba\",\"type\":\"project link out\",\"z\":\"FFF0000000000001\",\"g\":\"23fd40630dbef712\",\"name\":\"project out node to send the light status\",\"mode\":\"link\",\"broadcast\":true,\"project\":\"c51f38c2-6c80-442a-a9e2-10ddd68fb606\",\"topic\":\"light_status\",\"x\":840,\"y\":380,\"wires\":",[1107,7316],{},"},{\"id\":\"8d9a9dc4183a778e\",\"type\":\"buffer-parser\",\"z\":\"FFF0000000000001\",\"g\":\"23fd40630dbef712\",\"name\":\"\",\"data\":\"payload\",\"dataType\":\"msg\",\"specification\":\"spec\",\"specificationType\":\"ui\",\"items\":",[1107,7319,7320],{},"{\"type\":\"bool\",\"name\":\"red\",\"offset\":0,\"length\":1,\"offsetbit\":0,\"scale\":\"1\",\"mask\":\"\"},{\"type\":\"bool\",\"name\":\"yellow\",\"offset\":0,\"length\":1,\"offsetbit\":1,\"scale\":\"1\",\"mask\":\"\"},{\"type\":\"bool\",\"name\":\"green\",\"offset\":0,\"length\":1,\"offsetbit\":2,\"scale\":\"1\",\"mask\":\"\"},{\"type\":\"bool\",\"name\":\"all\",\"offset\":0,\"length\":16,\"offsetbit\":0,\"scale\":\"1\",\"mask\":\"\"}",",\"swap1\":\"\",\"swap2\":\"\",\"swap3\":\"\",\"swap1Type\":\"swap\",\"swap2Type\":\"swap\",\"swap3Type\":\"swap\",\"msgProperty\":\"payload\",\"msgPropertyType\":\"str\",\"resultType\":\"keyvalue\",\"resultTypeType\":\"return\",\"multipleResult\":false,\"fanOutMultipleResult\":false,\"setTopic\":true,\"outputs\":1,\"x\":530,\"y\":380,\"wires\":[[\"a8ea1622d1fad4ba\"]]},{\"id\":\"d60a74a5430df7ae\",\"type\":\"buffer-maker\",\"z\":\"FFF0000000000001\",\"g\":\"23fd40630dbef712\",\"name\":\"\",\"specification\":\"spec\",\"specificationType\":\"ui\",\"items\":",[1107,7323,7324],{},"{\"name\":\"1stword\",\"type\":\"uint16le\",\"length\":1,\"dataType\":\"msg\",\"data\":\"payload\"}",",\"swap1\":\"\",\"swap2\":\"\",\"swap3\":\"\",\"swap1Type\":\"swap\",\"swap2Type\":\"swap\",\"swap3Type\":\"swap\",\"msgProperty\":\"payload\",\"msgPropertyType\":\"str\",\"x\":330,\"y\":380,\"wires\":[[\"8d9a9dc4183a778e\"]]}",[15,7327,7328,7329,1413,7331,7334],{},"Below is the flow that you can import and deploy into the hosted instance created on FlowFuse. With this flow, you'll have a dashboard to control and monitor the tower lights. Just make sure you have installed ",[924,7330,1536],{},[924,7332,7333],{},"@flowfuse\u002Fnode-red-dashboard-2-ui-led",", and ensure the hosted instance is in the same FlowFuse team as your remote instance.",[15,7336,4917,7337,1150],{},[1107,7338,7339,7340,7343,7344,7346,7347,7349,7350,7352,7353,7355,7356,7358,7359,7362,7363,7366,7367,7369,7370,7373,7374,7376,7377,7380,7381,7383,7384,7387,7388,7391,7392,7395],{},"{\"id\":\"1f56099d53798b99\",\"type\":\"group\",\"z\":\"eb351e503901d04f\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,7341,7342],{},"\"1e6a379c83bac6b4\",\"f015125886fec5a6\",\"76c696f160db3ca2\",\"1974cfc417898151\",\"9c503fe31081dc2f\",\"9501e2eb7690a0b5\"",",\"x\":74,\"y\":79,\"w\":652,\"h\":202},{\"id\":\"1e6a379c83bac6b4\",\"type\":\"ui-button\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"group\":\"d4102809d229cb95\",\"name\":\"\",\"label\":\"YELLOW\",\"order\":5,\"width\":\"3\",\"height\":\"2\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"yellow\",\"textColor\":\"\",\"iconColor\":\"\",\"enableClick\":false,\"enablePointerdown\":true,\"pointerdownPayload\":\"1\",\"pointerdownPayloadType\":\"num\",\"enablePointerup\":true,\"pointerupPayload\":\"0\",\"pointerupPayloadType\":\"num\",\"x\":160,\"y\":180,\"wires\":[[\"1974cfc417898151\"]]},{\"id\":\"f015125886fec5a6\",\"type\":\"ui-button\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"group\":\"d4102809d229cb95\",\"name\":\"\",\"label\":\"RED\",\"order\":4,\"width\":\"3\",\"height\":\"2\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"red\",\"textColor\":\"\",\"iconColor\":\"\",\"enableClick\":false,\"enablePointerdown\":true,\"pointerdownPayload\":\"1\",\"pointerdownPayloadType\":\"num\",\"enablePointerup\":true,\"pointerupPayload\":\"0\",\"pointerupPayloadType\":\"num\",\"x\":150,\"y\":120,\"wires\":[[\"9501e2eb7690a0b5\"]]},{\"id\":\"76c696f160db3ca2\",\"type\":\"ui-button\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"group\":\"d4102809d229cb95\",\"name\":\"\",\"label\":\"GREEN\",\"order\":6,\"width\":\"3\",\"height\":\"2\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"green\",\"textColor\":\"\",\"iconColor\":\"\",\"enableClick\":false,\"enablePointerdown\":true,\"pointerdownPayload\":\"1\",\"pointerdownPayloadType\":\"num\",\"enablePointerup\":true,\"pointerupPayload\":\"0\",\"pointerupPayloadType\":\"num\",\"x\":160,\"y\":240,\"wires\":[[\"9c503fe31081dc2f\"]]},{\"id\":\"1974cfc417898151\",\"type\":\"project link out\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"name\":\"Project out node to control the yellow light\",\"mode\":\"link\",\"broadcast\":true,\"project\":\"c51f38c2-6c80-442a-a9e2-10ddd68fb606\",\"topic\":\"light_control_yellow\",\"x\":530,\"y\":180,\"wires\":",[1107,7345],{},"},{\"id\":\"9c503fe31081dc2f\",\"type\":\"project link out\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"name\":\"Project out node to control the green light\",\"mode\":\"link\",\"broadcast\":true,\"project\":\"c51f38c2-6c80-442a-a9e2-10ddd68fb606\",\"topic\":\"light_control_green\",\"x\":520,\"y\":240,\"wires\":",[1107,7348],{},"},{\"id\":\"9501e2eb7690a0b5\",\"type\":\"project link out\",\"z\":\"eb351e503901d04f\",\"g\":\"1f56099d53798b99\",\"name\":\"Project out node to control the red light\",\"mode\":\"link\",\"broadcast\":true,\"project\":\"c51f38c2-6c80-442a-a9e2-10ddd68fb606\",\"topic\":\"light_control_red\",\"x\":520,\"y\":120,\"wires\":",[1107,7351],{},"},{\"id\":\"d4102809d229cb95\",\"type\":\"ui-group\",\"name\":\"Group 1\",\"page\":\"62085b96f178f643\",\"width\":\"3\",\"height\":1,\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"62085b96f178f643\",\"type\":\"ui-page\",\"name\":\"Page 1\",\"ui\":\"02c25e8a30f9379d\",\"path\":\"\u002Fpage1\",\"icon\":\"home\",\"layout\":\"notebook\",\"theme\":\"f6f5e7ae33bf6878\",\"breakpoints\":",[1107,7354,2556],{},",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"02c25e8a30f9379d\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,7357,5343],{},",\"showPathInSidebar\":false,\"showPageTitle\":true,\"navigationStyle\":\"default\",\"titleBarStyle\":\"hidden\"},{\"id\":\"f6f5e7ae33bf6878\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094ce\",\"bgPage\":\"#1a1a1a\",\"groupBg\":\"#000000\",\"groupOutline\":\"#000000\"},\"sizes\":{\"density\":\"default\",\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"82cc6997fddd0b4b\",\"type\":\"group\",\"z\":\"eb351e503901d04f\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,7360,7361],{},"\"d163a7ab23f7458f\",\"20d211683534362b\",\"b1477193956e591b\",\"fb8801a4accc3c15\",\"2f62948afcfde259\",\"6966f129e718d20a\",\"a5cecf8e8adf6eef\"",",\"x\":74,\"y\":299,\"w\":872,\"h\":202},{\"id\":\"d163a7ab23f7458f\",\"type\":\"ui-led\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"Status of RED light\",\"group\":\"d4102809d229cb95\",\"order\":1,\"width\":\"1\",\"height\":\"3\",\"label\":\"\",\"labelPlacement\":\"left\",\"labelAlignment\":\"left\",\"states\":",[1107,7364,7365],{},"{\"value\":\"true\",\"valueType\":\"bool\",\"color\":\"#ff0000\"},{\"value\":\"false\",\"valueType\":\"bool\",\"color\":\"#787878\"}",",\"allowColorForValueInMessage\":false,\"shape\":\"circle\",\"showBorder\":true,\"showGlow\":true,\"x\":810,\"y\":340,\"wires\":",[1107,7368],{},"},{\"id\":\"20d211683534362b\",\"type\":\"ui-led\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"Status of Yellow light\",\"group\":\"d4102809d229cb95\",\"order\":2,\"width\":\"1\",\"height\":\"3\",\"label\":\"\",\"labelPlacement\":\"left\",\"labelAlignment\":\"left\",\"states\":",[1107,7371,7372],{},"{\"value\":\"true\",\"valueType\":\"bool\",\"color\":\"#c8ff00\"},{\"value\":\"false\",\"valueType\":\"bool\",\"color\":\"#787878\"}",",\"allowColorForValueInMessage\":false,\"shape\":\"circle\",\"showBorder\":true,\"showGlow\":true,\"x\":820,\"y\":400,\"wires\":",[1107,7375],{},"},{\"id\":\"b1477193956e591b\",\"type\":\"ui-led\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"Status of Green light\",\"group\":\"d4102809d229cb95\",\"order\":3,\"width\":\"1\",\"height\":\"3\",\"label\":\"\",\"labelPlacement\":\"left\",\"labelAlignment\":\"left\",\"states\":",[1107,7378,7379],{},"{\"value\":\"true\",\"valueType\":\"bool\",\"color\":\"#41891a\"},{\"value\":\"false\",\"valueType\":\"bool\",\"color\":\"#787878\"}",",\"allowColorForValueInMessage\":false,\"shape\":\"circle\",\"showBorder\":true,\"showGlow\":true,\"x\":820,\"y\":460,\"wires\":",[1107,7382],{},"},{\"id\":\"fb8801a4accc3c15\",\"type\":\"change\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"\",\"rules\":",[1107,7385,7386],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.red\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":600,\"y\":340,\"wires\":[[\"d163a7ab23f7458f\"]]},{\"id\":\"2f62948afcfde259\",\"type\":\"change\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"\",\"rules\":",[1107,7389,7390],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.yellow\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":600,\"y\":400,\"wires\":[[\"20d211683534362b\"]]},{\"id\":\"6966f129e718d20a\",\"type\":\"change\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"\",\"rules\":",[1107,7393,7394],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.green\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":600,\"y\":460,\"wires\":[[\"b1477193956e591b\"]]},{\"id\":\"a5cecf8e8adf6eef\",\"type\":\"project link in\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"project in node to receive the light status\",\"project\":\"all\",\"broadcast\":true,\"topic\":\"light_status\",\"x\":260,\"y\":400,\"wires\":[[\"fb8801a4accc3c15\",\"2f62948afcfde259\",\"6966f129e718d20a\"]]}",[31,7397,7399],{"id":7398},"troubleshooting","Troubleshooting",[15,7401,7402],{},"When you try to establish a connection with the PLC, you may encounter the following error. This error occurs because your device has established the connection but is unable to communicate. To resolve this issue, ensure that you have configured all the settings mentioned in the prerequisites. If the problem persists, it could be because your PLC and the device running Node-RED are on different networks.",[15,7404,7405,7409],{},[165,7406],{"alt":7407,"src":7408,"dataZoomable":103},"\"Error: This service is not implemented on the modeul or frame error was reported\"","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Ferror.png",[170,7410,7407],{},[15,7412,7413],{},"Make sure the IP addresses of your device and PLC are in the same subnet. If the PLC is connected to the internet via a router, all devices (PLC, Node-RED device, and router) should have IP addresses within the same subnet. For example, if your PLC has the address 192.168.1.1, ensure that the other devices have IP addresses in the range 192.168.1.x.",[31,7415,1425],{"id":1424},[15,7417,7418],{},"Integrating Siemens S7 PLCs with Node-RED opens up powerful automation possibilities with minimal complexity. By following the steps outlined in this guide, you can easily connect your PLC to Node-RED, control devices, and visualize real-time data on dashboards. Whether you're writing data to control outputs or reading sensor values, Node-RED offers a flexible, user-friendly platform for industrial automation.",[15,7420,7421,7422,7424],{},"Beyond Siemens S7, FlowFuse connects Allen-Bradley, Omron, Beckhoff, and any Modbus or OPC UA-enabled PLC to MQTT, cloud, and enterprise systems. See the ",[63,7423,3684],{"href":3683}," for all supported protocols and use cases.",{"title":103,"searchDepth":104,"depth":104,"links":7426},[7427,7428,7437,7438],{"id":6239,"depth":107,"text":6240},{"id":6309,"depth":107,"text":6310,"children":7429},[7430,7433,7434,7435,7436],{"id":6330,"depth":294,"text":6331,"children":7431},[7432],{"id":6346,"depth":104,"text":6347},{"id":6370,"depth":294,"text":6371},{"id":7079,"depth":294,"text":7080},{"id":7140,"depth":294,"text":7141},{"id":7192,"depth":294,"text":7193},{"id":7398,"depth":107,"text":7399},{"id":1424,"depth":107,"text":1425},{"type":318,"title":7440,"description":7441},"Turn PLC Data Into Operational Visibility with FlowFuse","Reading and writing S7 data is just the start. FlowFuse extends your existing PLCs, SCADA, and MES, no rip-and-replace, into a single platform for remote device management, dashboards, and DevOps pipelines. Connect Siemens, Allen-Bradley, Modbus, and OPC UA devices, and get your first operational application running this week. SOC 2 certified, with RBAC, SSO, and self-hosted options. Talk to our team about your architecture.","2025-01-17","Learn how to read data from and write data to Siemens S7 PLCs (S7-1200\u002F1500) for industrial monitoring and control. This guide covers PLC setup, the S7 protocol over ISO-on-TCP, addressing, and building dashboards, no deep PLC expertise required.","\u002Fblog\u002F2025\u002F01\u002Fimages\u002Fs7-with-node-red.png",{"keywords":7446,"excerpt":7447},"siemens s7 plc data, read data from s7 plc, write data to s7 plc, siemens s7 1200 with node-red, siemens s7 1500 with node-red, s7 with node-red, plc data integration, industrial automation",{"type":12,"value":7448},[7449],[15,7450,6230],{},"\u002Fblog\u002F2025\u002F01\u002Fintegrating-siemens-s7-plcs-with-node-red-guide",{"title":6223,"description":7443},{"loc":7451},"blog\u002F2025\u002F01\u002Fintegrating-siemens-s7-plcs-with-node-red-guide","A step-by-step beginner's guide to reading, writing, and monitoring data from Siemens S7-1200 and S7-1500 PLCs.",[138,10,3725,7457],"how-to","This guide explains how to read data from and write data to Siemens S7 PLCs (S7-1200\u002F1500) using Node-RED and the S7 protocol over ISO-on-TCP (port 102). Prerequisites: enable PUT\u002FGET communication, disable optimized block access on your data blocks, and run Node-RED on a networked device (the FlowFuse Device Agent makes this remotely manageable). After installing an S7 node, you configure an endpoint with the PLC's IP, rack, and slot, map TIA Portal addresses to the node's address format, then use s7-in to read values and s7-out to write them, enabling remote monitoring, control, and dashboards without deep PLC expertise. The same patterns extend to Allen-Bradley, Modbus, and OPC UA devices for multi-vendor environments.","Zumw3mIy-PpnM75KupEm8sJVEiDdkOzuF4B2qYSxufk",{"id":7461,"title":7462,"authors":7463,"body":7464,"cta":8391,"date":8395,"description":8396,"extension":116,"image":8397,"lastUpdated":6203,"meta":8398,"navigation":129,"path":8404,"seo":8405,"sitemap":8406,"stem":8407,"subtitle":8408,"tags":8409,"tldr":8411,"video":3,"__hash__":8412},"blog\u002Fblog\u002F2024\u002F11\u002Fesp32-with-node-red.md","Interacting with ESP32 Using Node-RED and MQTT (2026)",[144],{"type":12,"value":7465,"toc":8380},[7466,7469,7472,7474,7476,7518,7521,7525,7528,7532,7542,7545,7549,7552,7555,7564,7567,7572,7595,7603,7614,7618,7621,7626,7680,7686,7718,7734,7737,7745,7749,7756,7761,7780,7785,7790,8305,8316,8319,8328,8331,8333,8355,8360,8372,8374,8377],[15,7467,7468],{},"The ESP32 is an affordable and powerful microchip that combines Wi-Fi and Bluetooth in one small package. It's commonly used in smart devices like home automation systems, wearables, and other IoT projects. Despite its low cost (around $6), it offers strong performance, and low power consumption, and is compatible with popular platforms like Arduino. Whether you're a hobbyist or a business, the ESP32 provides great value, making it easy to create wireless devices without a big investment. This tutorial demonstrates how to set up communication between the ESP32 and Node-RED using MQTT, along with an interactive dashboard via FlowFuse for a user-friendly interface.",[441,7470],{"videoid":7471,"params":464,"style":4625,"title":3470},"ecfJ-9MxyVE",[31,7473,820],{"id":819},[15,7475,5653],{},[39,7477,7478,7484,7490,7512],{},[42,7479,7480,7483],{},[19,7481,7482],{},"ESP32 microcontroller",": The hardware you'll be using for this project.",[42,7485,7486,7489],{},[19,7487,7488],{},"USB cable",": To connect the ESP32 to your computer.",[42,7491,7492,7495,7496,7501,7502],{},[19,7493,7494],{},"Arduino IDE",": Installed and set up to program your ESP32. ",[63,7497,7500],{"href":7498,"rel":7499},"https:\u002F\u002Fsupport.arduino.cc\u002Fhc\u002Fen-us\u002Farticles\u002F360019833020-Download-and-install-Arduino-IDE",[67],"Download"," the Arduino IDE if you haven't already done so.\n",[39,7503,7504],{},[42,7505,7506,7507],{},"Additionally, if you haven't set up the Arduino IDE for the ESP32 board, please follow this tutorial: ",[63,7508,7511],{"href":7509,"rel":7510},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=CD8VJl27n94",[67],"How to Set Up ESP32 with Arduino IDE",[42,7513,7514,7517],{},[19,7515,7516],{},"FlowFuse account",": This will allow you to create and deploy Node-RED instances securely on the cloud with a single click, collaborate on your Node-RED projects with your team, manage and program your edge devices remotely, and provide an MQTT broker with an interface for securely managing clients.",[15,7519,7520],{},"If you haven’t signed up for a FlowFuse account yet, [sign up]({% include \"sign-up-url.njk\" %}?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Interacting%20with%20ESP32%20Using%20Node-RED%20and%20MQTT) now.",[31,7522,7524],{"id":7523},"getting-started-with-esp32-and-node-red","Getting Started with ESP32 and Node-RED",[15,7526,7527],{},"In this section, we’ll set up Node-RED on FlowFuse, create an MQTT connection, and configure everything to interact with your ESP32. This will lay the foundation for building your IoT flows and controlling devices.",[399,7529,7531],{"id":7530},"step-1-creating-node-red-instance-on-flowfuse-cloud","Step 1: Creating Node-RED instance on FlowFuse Cloud",[15,7533,7534,7535,7537,7538,29],{},"Start by logging into your ",[63,7536,803],{"href":810}," account and creating a new Node-RED instance. For more information on creating a Node-RED instance, refer to the ",[63,7539,7541],{"href":7540},"\u002Fdocs\u002Fuser\u002Fintroduction\u002F#creating-a-node-red-instance","FlowFuse documentation",[15,7543,7544],{},"Once the instance is created, open the Node-RED editor.",[399,7546,7548],{"id":7547},"step-2-creating-and-configuring-mqtt-clients-in-flowfuse","Step 2: Creating and Configuring MQTT Clients in FlowFuse",[15,7550,7551],{},"In this step, we’ll set up MQTT to enable communication between Node-RED and the ESP32. MQTT (Message Queuing Telemetry Transport) is a lightweight messaging protocol designed for reliable, low-bandwidth communication between devices in IoT applications.",[15,7553,7554],{},"We use MQTT because it allows devices to communicate over a network (like Wi-Fi) without the need for a direct physical connection. This makes it perfect for long-distance communication, where devices need to send and receive data efficiently, even when they are not physically connected or close to each other.",[15,7556,7557,7561],{},[165,7558],{"alt":7559,"dataZoomable":103,"src":7560},"Diagram showing the flow of data and how commands are sent to the ESP32 using MQTT using Node-RED.","\u002Fblog\u002F2024\u002F11\u002Fimages\u002Fesp32-mqtt-node-red.png",[170,7562,7563],{},"Diagram showing the flow of data and how commands are sent to the ESP32 using MQTT using Node-RED",[15,7565,7566],{},"In our setup, Node-RED will publish commands to the MQTT broker, and the ESP32 will subscribe to topics to receive responses. The ESP32 will then perform actions, such as controlling an LED. To facilitate this, we’ll create two MQTT clients in FlowFuse (since the MQTT broker is already set up and managed by FlowFuse, you don’t need to worry about its configuration or maintenance). One client will be for Node-RED, and the other will be for the ESP32. These clients will handle the secure and reliable exchange of messages, ensuring smooth communication between the two devices.",[15,7568,7569],{},[19,7570,7571],{},"To Create MQTT Clients in FlowFuse:",[271,7573,7574,7577,7580,7583],{},[42,7575,7576],{},"Navigate to your FlowFuse platform and log in to your account.",[42,7578,7579],{},"In the left sidebar, click on \"Broker\".",[42,7581,7582],{},"In the newly opened interface, click the “Create Client” button.",[42,7584,7585,7586],{},"Enter a username and password for your MQTT client. Confirm the password.\n",[39,7587,7588],{},[42,7589,7590,7591,7594],{},"You can leave the default pattern as ",[924,7592,7593],{},"#"," for access control, or set a custom pattern if needed.",[15,7596,7597,7601],{},[165,7598],{"alt":7599,"dataZoomable":103,"src":7600},"Interface for setting MQTT client details and credentials","\u002Fblog\u002F2024\u002F11\u002Fimages\u002Fmqtt-client-create.png",[170,7602,7599],{},[271,7604,7605,7608,7611],{"start":1868},[42,7606,7607],{},"Click \"Create\" to generate the client.",[42,7609,7610],{},"Copy the client ID and save it somewhere secure for later use.",[42,7612,7613],{},"Repeat the same steps to create the second MQTT client for the ESP32.",[399,7615,7617],{"id":7616},"step-3-building-a-node-red-dashboard-to-send-commands-over-mqtt","Step 3: Building a Node-RED Dashboard to Send Commands Over MQTT",[15,7619,7620],{},"Now that we’ve created the MQTT clients, it’s time to build a Node-RED dashboard and create a flow that will publish commands to the FlowFuse MQTT broker. This will later allow you to interact with your ESP32 using a user-friendly interface.",[15,7622,7623],{},[19,7624,7625],{},"Let's first create a flow to connect to the MQTT broker with the client config we have created:",[271,7627,7628,7634,7639,7642,7662,7665,7671,7674,7677],{},[42,7629,5054,7630,7633],{},[19,7631,7632],{},"mqtt out"," node onto the canvas in Node-RED.",[42,7635,1641,7636,7638],{},[19,7637,7632],{}," node to open the settings.",[42,7640,7641],{},"Click the pencil icon next to the Server field to open the MQTT broker configuration.",[42,7643,7644,7645],{},"In the configuration, enter the following details:\n",[39,7646,7647,7653,7656,7659],{},[42,7648,7649,7650],{},"Server: ",[924,7651,7652],{},"broker.flowfuse.cloud",[42,7654,7655],{},"Client ID: The Client ID you created earlier.",[42,7657,7658],{},"Username: The MQTT username (Client ID).",[42,7660,7661],{},"Password: The MQTT password.",[42,7663,7664],{},"Click \"Add\" to save the configuration, then select the newly added configuration.",[42,7666,7667,7668,29],{},"In the Topic field, enter a topic name, such as ",[924,7669,7670],{},"\u002FLEDControl",[42,7672,7673],{},"Click \"Done\" to close the settings.",[42,7675,7676],{},"Click \"Deploy\" in the top-right corner to deploy the flow.",[42,7678,7679],{},"Once deployed, check the MQTT out node for a Connected status, confirming the connection to the MQTT broker.",[15,7681,7682,7683],{},"For this example, we will create a very simple dashboard. If you're not familiar with FlowFuse Dashboard, you can refer to the following blog to get started: ",[63,7684,7685],{"href":3671},"FlowFuse Dashboard: Getting Started",[271,7687,7688,7693,7699,7705,7710,7715],{},[42,7689,1171,7690,7692],{},[924,7691,1536],{}," from the Node-RED Palette Manager.",[42,7694,5082,7695,7698],{},[19,7696,7697],{},"ui-button"," widgets onto the canvas.",[42,7700,7701,7702,7704],{},"Double-click on the first button and set the Label to \"ON\", the Background Color to Green, and the Payload to ",[924,7703,4944],{},". Adjust the Width and Height as needed.",[42,7706,7707,7708,29],{},"Double-click on the second button and set the Label to \"OFF\", the Background Color to Red, and the Payload to ",[924,7709,4948],{},[42,7711,7712,7713,1318],{},"Connect the output of both buttons to the input of the ",[19,7714,7632],{},[42,7716,7717],{},"Click \"Deploy\" to save the flow.",[15,7719,4917,7720,1150],{},[1107,7721,7722,7723,7725,7726,7728,7729,7731,7732,4957],{},"{\"id\":\"59887a8115c95eae\",\"type\":\"tab\",\"label\":\"Flow 1\",\"disabled\":false,\"info\":\"\",\"env\":",[1107,7724],{},"},{\"id\":\"02c25e8a30f9379d\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,7727,5343],{},",\"showPathInSidebar\":false,\"showPageTitle\":true,\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\"},{\"id\":\"cfb2ab9ff30660fc\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094CE\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"density\":\"default\",\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"d263574af6876c7a\",\"type\":\"ui-page\",\"name\":\"ESP32\",\"ui\":\"02c25e8a30f9379d\",\"path\":\"\u002Fpage1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"cfb2ab9ff30660fc\",\"breakpoints\":",[1107,7730,2556],{},",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"3ae115ea7ede6827\",\"type\":\"ui-group\",\"name\":\"Group 1\",\"page\":\"d263574af6876c7a\",\"width\":\"6\",\"height\":\"1\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"def97b29f5f7baab\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"broker.flowfuse.cloud\",\"port\":\"1883\",\"clientid\":\"\",\"autoConnect\":true,\"usetls\":false,\"protocolVersion\":\"4\",\"keepalive\":\"60\",\"cleansession\":true,\"autoUnsubscribe\":true,\"birthTopic\":\"\",\"birthQos\":\"0\",\"birthRetain\":\"false\",\"birthPayload\":\"\",\"birthMsg\":{},\"closeTopic\":\"\",\"closeQos\":\"0\",\"closeRetain\":\"false\",\"closePayload\":\"\",\"closeMsg\":{},\"willTopic\":\"\",\"willQos\":\"0\",\"willRetain\":\"false\",\"willPayload\":\"\",\"willMsg\":{},\"userProps\":\"\",\"sessionExpiry\":\"\"},{\"id\":\"5a9162986a34a4d6\",\"type\":\"ui-button\",\"z\":\"59887a8115c95eae\",\"group\":\"3ae115ea7ede6827\",\"name\":\"\",\"label\":\"ON\",\"order\":1,\"width\":\"3\",\"height\":\"2\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"1\",\"payloadType\":\"num\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"green\",\"textColor\":\"\",\"iconColor\":\"\",\"enableClick\":true,\"enablePointerdown\":false,\"pointerdownPayload\":\"\",\"pointerdownPayloadType\":\"str\",\"enablePointerup\":false,\"pointerupPayload\":\"\",\"pointerupPayloadType\":\"str\",\"x\":190,\"y\":120,\"wires\":[[\"9239f8a7cca5c858\"]]},{\"id\":\"f9c194994d9491a8\",\"type\":\"ui-button\",\"z\":\"59887a8115c95eae\",\"group\":\"3ae115ea7ede6827\",\"name\":\"\",\"label\":\"OFF\",\"order\":2,\"width\":\"3\",\"height\":\"2\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"2\",\"payloadType\":\"num\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"red\",\"textColor\":\"\",\"iconColor\":\"\",\"enableClick\":true,\"enablePointerdown\":false,\"pointerdownPayload\":\"\",\"pointerdownPayloadType\":\"str\",\"enablePointerup\":false,\"pointerupPayload\":\"\",\"pointerupPayloadType\":\"str\",\"x\":190,\"y\":160,\"wires\":[[\"9239f8a7cca5c858\"]]},{\"id\":\"9239f8a7cca5c858\",\"type\":\"mqtt out\",\"z\":\"59887a8115c95eae\",\"name\":\"\",\"topic\":\"\u002FLedControl\",\"qos\":\"\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"def97b29f5f7baab\",\"x\":390,\"y\":140,\"wires\":",[1107,7733],{},[15,7735,7736],{},"Now, when you click either the \"ON\" or \"OFF\" button on the dashboard, it will send either 1 or 2 as the payload. The ESP32 will use this payload in its code to turn the LED on or off. To view the dashboard, switch to the Dashboard 2.0 tab on the right side and click the Open Dashboard button. The dashboard will look similar to the image below.",[15,7738,7739,7743],{},[165,7740],{"alt":7741,"dataZoomable":103,"src":7742},"FlowFuse Dashboard Build to control the ESP32 LED","\u002Fblog\u002F2024\u002F11\u002Fimages\u002Fdashboard2.png",[170,7744,7741],{},[399,7746,7748],{"id":7747},"step-4-programming-esp32-to-receive-commands-from-mqtt-and-control-led","Step 4: Programming ESP32 to receive commands from MQTT and Control LED",[15,7750,7751,7752,7755],{},"Now, let's move on to the final step. Before proceeding, make sure your ESP32 is ",[19,7753,7754],{},"connected to your laptop or computer via USB","*. The USB connection is essential for uploading the code (sketch) to the ESP32, which will enable it to connect to the internet and communicate with the MQTT broker.\nThe ESP32 will subscribe to the MQTT topic we configured earlier (e.g., \u002FLEDControl). Based on the received payload (1 or 2), it will control the LED accordingly, turning it on or off.",[15,7757,7758],{},[19,7759,7760],{},"Setting up Arduino IDE:",[271,7762,7763,7765,7768,7771,7774,7777],{},[42,7764,5720],{},[42,7766,7767],{},"Ensure you have selected the correct board and port in the Tools menu.",[42,7769,7770],{},"Install the necessary library:",[42,7772,7773],{},"Go to Tools > \"Manage Libraries\".",[42,7775,7776],{},"Search for and install the \"EspMQTTClient\" library by Patrick Lapointe.",[42,7778,7779],{},"The library installation will prompt you to install its dependencies, ensure that you tick that option and proceed to install.",[15,7781,7782],{},[19,7783,7784],{},"Code for ESP32:",[271,7786,7787],{},[42,7788,7789],{},"Copy the following code into the Arduino IDE:",[1756,7791,7795],{"className":7792,"code":7793,"language":7794,"meta":103,"style":103},"language-cpp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","#if defined(ESP32)\n#include \u003CWiFi.h>\n#elif defined(ESP8266)\n#include \u003CESP8266WiFi.h>\n#endif\n\n#include \u003CPubSubClient.h>\n\n#define LedPin 2 \u002F\u002F ESP32 built-in LED pin\n\n\u002F\u002F WiFi and MQTT settings\nconst char* ssid = \"\"; \u002F\u002F Change this to your WiFi SSID\nconst char* password = \"\"; \u002F\u002F Change this to your WiFi password\nconst char* mqtt_server = \"broker.flowfuse.cloud\"; \u002F\u002F FlowFuse MQTT broker server\n\n\u002F\u002F MQTT client credentials\nconst char* mqtt_client_id = \"\"; \u002F\u002F Replace with your MQTT client ID\nconst char* mqtt_username = \"\"; \u002F\u002F Replace with your MQTT username\nconst char* mqtt_password = \"\"; \u002F\u002F Replace with your MQTT password\n\nWiFiClient espClient;\nPubSubClient client(espClient);\n\n\u002F\u002F Function to connect to WiFi\nvoid setup_wifi() {\n    delay(10);\n    Serial.println();\n    Serial.print(\"Connecting to \");\n    Serial.print(ssid);\n    WiFi.begin(ssid, password);\n\n    while(WiFi.status() != WL_CONNECTED) {\n        delay(500);\n        Serial.print(\".\");\n }\n\n    Serial.println(\"\\nWiFi connected\");\n    Serial.print(\"IP address: \");\n    Serial.println(WiFi.localIP());\n}\n\n\u002F\u002F Callback function to handle messages from subscribed topics\nvoid callback(char* topic, byte* payload, unsigned int length) {\n\n String msg;\n    for (int i = 0; i \u003C length; i++) {\n msg += (char)payload[i];\n }\n\n \u002F\u002F Control LED based on message\n    if (msg == \"1\") {\n        digitalWrite(LedPin, HIGH); \u002F\u002F Turn LED on\n }\n    else if (msg == \"2\") {\n        digitalWrite(LedPin, LOW); \u002F\u002F Turn LED off\n }\n}\n\n\u002F\u002F Function to connect to MQTT broker\nvoid reconnect() {\n    while (!client.connected()) {\n        Serial.println(\"Attempting MQTT connection...\");\n\n \u002F\u002F Connect to MQTT broker with the client ID, username, and password\n        if (client.connect(mqtt_client_id, mqtt_username, mqtt_password)) {\n            Serial.println(\"Connected to MQTT broker\");\n            client.subscribe(\"\u002FLedControl\");\n }\n        else {\n            Serial.print(\"Failed, rc=\");\n            Serial.print(client.state());\n            Serial.println(\" trying again in 5 seconds\");\n            delay(5000);\n }\n }\n}\n\nvoid setup() {\n    Serial.begin(115200);\n    pinMode(LedPin, OUTPUT);\n    setup_wifi();\n    client.setServer(mqtt_server, 1883);\n    client.setCallback(callback);\n}\n\nvoid loop() {\n    if (!client.connected()) {\n        reconnect();\n }\n    client.loop();\n}\n","cpp",[924,7796,7797,7802,7807,7812,7817,7822,7826,7831,7835,7843,7847,7852,7857,7862,7867,7871,7877,7883,7889,7895,7900,7906,7912,7917,7923,7929,7935,7941,7947,7953,7959,7964,7970,7976,7982,7988,7993,7999,8005,8011,8017,8022,8028,8034,8039,8045,8051,8057,8062,8067,8073,8079,8085,8090,8096,8102,8107,8112,8117,8123,8129,8135,8141,8146,8152,8158,8164,8170,8175,8181,8187,8193,8199,8205,8210,8215,8220,8225,8231,8237,8243,8249,8255,8261,8266,8271,8277,8283,8289,8294,8300],{"__ignoreMap":103},[1107,7798,7799],{"class":1765,"line":1766},[1107,7800,7801],{},"#if defined(ESP32)\n",[1107,7803,7804],{"class":1765,"line":107},[1107,7805,7806],{},"#include \u003CWiFi.h>\n",[1107,7808,7809],{"class":1765,"line":294},[1107,7810,7811],{},"#elif defined(ESP8266)\n",[1107,7813,7814],{"class":1765,"line":104},[1107,7815,7816],{},"#include \u003CESP8266WiFi.h>\n",[1107,7818,7819],{"class":1765,"line":1868},[1107,7820,7821],{},"#endif\n",[1107,7823,7824],{"class":1765,"line":1896},[1107,7825,1921],{"emptyLinePlaceholder":129},[1107,7827,7828],{"class":1765,"line":1623},[1107,7829,7830],{},"#include \u003CPubSubClient.h>\n",[1107,7832,7833],{"class":1765,"line":1924},[1107,7834,1921],{"emptyLinePlaceholder":129},[1107,7836,7837,7840],{"class":1765,"line":1930},[1107,7838,7839],{},"#define LedPin 2",[1107,7841,7842],{}," \u002F\u002F ESP32 built-in LED pin\n",[1107,7844,7845],{"class":1765,"line":1940},[1107,7846,1921],{"emptyLinePlaceholder":129},[1107,7848,7849],{"class":1765,"line":1976},[1107,7850,7851],{},"\u002F\u002F WiFi and MQTT settings\n",[1107,7853,7854],{"class":1765,"line":2002},[1107,7855,7856],{},"const char* ssid = \"\"; \u002F\u002F Change this to your WiFi SSID\n",[1107,7858,7859],{"class":1765,"line":2029},[1107,7860,7861],{},"const char* password = \"\"; \u002F\u002F Change this to your WiFi password\n",[1107,7863,7864],{"class":1765,"line":1691},[1107,7865,7866],{},"const char* mqtt_server = \"broker.flowfuse.cloud\"; \u002F\u002F FlowFuse MQTT broker server\n",[1107,7868,7869],{"class":1765,"line":2080},[1107,7870,1921],{"emptyLinePlaceholder":129},[1107,7872,7874],{"class":1765,"line":7873},16,[1107,7875,7876],{},"\u002F\u002F MQTT client credentials\n",[1107,7878,7880],{"class":1765,"line":7879},17,[1107,7881,7882],{},"const char* mqtt_client_id = \"\"; \u002F\u002F Replace with your MQTT client ID\n",[1107,7884,7886],{"class":1765,"line":7885},18,[1107,7887,7888],{},"const char* mqtt_username = \"\"; \u002F\u002F Replace with your MQTT username\n",[1107,7890,7892],{"class":1765,"line":7891},19,[1107,7893,7894],{},"const char* mqtt_password = \"\"; \u002F\u002F Replace with your MQTT password\n",[1107,7896,7898],{"class":1765,"line":7897},20,[1107,7899,1921],{"emptyLinePlaceholder":129},[1107,7901,7903],{"class":1765,"line":7902},21,[1107,7904,7905],{},"WiFiClient espClient;\n",[1107,7907,7909],{"class":1765,"line":7908},22,[1107,7910,7911],{},"PubSubClient client(espClient);\n",[1107,7913,7915],{"class":1765,"line":7914},23,[1107,7916,1921],{"emptyLinePlaceholder":129},[1107,7918,7920],{"class":1765,"line":7919},24,[1107,7921,7922],{},"\u002F\u002F Function to connect to WiFi\n",[1107,7924,7926],{"class":1765,"line":7925},25,[1107,7927,7928],{},"void setup_wifi() {\n",[1107,7930,7932],{"class":1765,"line":7931},26,[1107,7933,7934],{},"    delay(10);\n",[1107,7936,7938],{"class":1765,"line":7937},27,[1107,7939,7940],{},"    Serial.println();\n",[1107,7942,7944],{"class":1765,"line":7943},28,[1107,7945,7946],{},"    Serial.print(\"Connecting to \");\n",[1107,7948,7950],{"class":1765,"line":7949},29,[1107,7951,7952],{},"    Serial.print(ssid);\n",[1107,7954,7956],{"class":1765,"line":7955},30,[1107,7957,7958],{},"    WiFi.begin(ssid, password);\n",[1107,7960,7962],{"class":1765,"line":7961},31,[1107,7963,1921],{"emptyLinePlaceholder":129},[1107,7965,7967],{"class":1765,"line":7966},32,[1107,7968,7969],{},"    while(WiFi.status() != WL_CONNECTED) {\n",[1107,7971,7973],{"class":1765,"line":7972},33,[1107,7974,7975],{},"        delay(500);\n",[1107,7977,7979],{"class":1765,"line":7978},34,[1107,7980,7981],{},"        Serial.print(\".\");\n",[1107,7983,7985],{"class":1765,"line":7984},35,[1107,7986,7987],{}," }\n",[1107,7989,7991],{"class":1765,"line":7990},36,[1107,7992,1921],{"emptyLinePlaceholder":129},[1107,7994,7996],{"class":1765,"line":7995},37,[1107,7997,7998],{},"    Serial.println(\"\\nWiFi connected\");\n",[1107,8000,8002],{"class":1765,"line":8001},38,[1107,8003,8004],{},"    Serial.print(\"IP address: \");\n",[1107,8006,8008],{"class":1765,"line":8007},39,[1107,8009,8010],{},"    Serial.println(WiFi.localIP());\n",[1107,8012,8014],{"class":1765,"line":8013},40,[1107,8015,8016],{},"}\n",[1107,8018,8020],{"class":1765,"line":8019},41,[1107,8021,1921],{"emptyLinePlaceholder":129},[1107,8023,8025],{"class":1765,"line":8024},42,[1107,8026,8027],{},"\u002F\u002F Callback function to handle messages from subscribed topics\n",[1107,8029,8031],{"class":1765,"line":8030},43,[1107,8032,8033],{},"void callback(char* topic, byte* payload, unsigned int length) {\n",[1107,8035,8037],{"class":1765,"line":8036},44,[1107,8038,1921],{"emptyLinePlaceholder":129},[1107,8040,8042],{"class":1765,"line":8041},45,[1107,8043,8044],{}," String msg;\n",[1107,8046,8048],{"class":1765,"line":8047},46,[1107,8049,8050],{},"    for (int i = 0; i \u003C length; i++) {\n",[1107,8052,8054],{"class":1765,"line":8053},47,[1107,8055,8056],{}," msg += (char)payload[i];\n",[1107,8058,8060],{"class":1765,"line":8059},48,[1107,8061,7987],{},[1107,8063,8065],{"class":1765,"line":8064},49,[1107,8066,1921],{"emptyLinePlaceholder":129},[1107,8068,8070],{"class":1765,"line":8069},50,[1107,8071,8072],{}," \u002F\u002F Control LED based on message\n",[1107,8074,8076],{"class":1765,"line":8075},51,[1107,8077,8078],{},"    if (msg == \"1\") {\n",[1107,8080,8082],{"class":1765,"line":8081},52,[1107,8083,8084],{},"        digitalWrite(LedPin, HIGH); \u002F\u002F Turn LED on\n",[1107,8086,8088],{"class":1765,"line":8087},53,[1107,8089,7987],{},[1107,8091,8093],{"class":1765,"line":8092},54,[1107,8094,8095],{},"    else if (msg == \"2\") {\n",[1107,8097,8099],{"class":1765,"line":8098},55,[1107,8100,8101],{},"        digitalWrite(LedPin, LOW); \u002F\u002F Turn LED off\n",[1107,8103,8105],{"class":1765,"line":8104},56,[1107,8106,7987],{},[1107,8108,8110],{"class":1765,"line":8109},57,[1107,8111,8016],{},[1107,8113,8115],{"class":1765,"line":8114},58,[1107,8116,1921],{"emptyLinePlaceholder":129},[1107,8118,8120],{"class":1765,"line":8119},59,[1107,8121,8122],{},"\u002F\u002F Function to connect to MQTT broker\n",[1107,8124,8126],{"class":1765,"line":8125},60,[1107,8127,8128],{},"void reconnect() {\n",[1107,8130,8132],{"class":1765,"line":8131},61,[1107,8133,8134],{},"    while (!client.connected()) {\n",[1107,8136,8138],{"class":1765,"line":8137},62,[1107,8139,8140],{},"        Serial.println(\"Attempting MQTT connection...\");\n",[1107,8142,8144],{"class":1765,"line":8143},63,[1107,8145,1921],{"emptyLinePlaceholder":129},[1107,8147,8149],{"class":1765,"line":8148},64,[1107,8150,8151],{}," \u002F\u002F Connect to MQTT broker with the client ID, username, and password\n",[1107,8153,8155],{"class":1765,"line":8154},65,[1107,8156,8157],{},"        if (client.connect(mqtt_client_id, mqtt_username, mqtt_password)) {\n",[1107,8159,8161],{"class":1765,"line":8160},66,[1107,8162,8163],{},"            Serial.println(\"Connected to MQTT broker\");\n",[1107,8165,8167],{"class":1765,"line":8166},67,[1107,8168,8169],{},"            client.subscribe(\"\u002FLedControl\");\n",[1107,8171,8173],{"class":1765,"line":8172},68,[1107,8174,7987],{},[1107,8176,8178],{"class":1765,"line":8177},69,[1107,8179,8180],{},"        else {\n",[1107,8182,8184],{"class":1765,"line":8183},70,[1107,8185,8186],{},"            Serial.print(\"Failed, rc=\");\n",[1107,8188,8190],{"class":1765,"line":8189},71,[1107,8191,8192],{},"            Serial.print(client.state());\n",[1107,8194,8196],{"class":1765,"line":8195},72,[1107,8197,8198],{},"            Serial.println(\" trying again in 5 seconds\");\n",[1107,8200,8202],{"class":1765,"line":8201},73,[1107,8203,8204],{},"            delay(5000);\n",[1107,8206,8208],{"class":1765,"line":8207},74,[1107,8209,7987],{},[1107,8211,8213],{"class":1765,"line":8212},75,[1107,8214,7987],{},[1107,8216,8218],{"class":1765,"line":8217},76,[1107,8219,8016],{},[1107,8221,8223],{"class":1765,"line":8222},77,[1107,8224,1921],{"emptyLinePlaceholder":129},[1107,8226,8228],{"class":1765,"line":8227},78,[1107,8229,8230],{},"void setup() {\n",[1107,8232,8234],{"class":1765,"line":8233},79,[1107,8235,8236],{},"    Serial.begin(115200);\n",[1107,8238,8240],{"class":1765,"line":8239},80,[1107,8241,8242],{},"    pinMode(LedPin, OUTPUT);\n",[1107,8244,8246],{"class":1765,"line":8245},81,[1107,8247,8248],{},"    setup_wifi();\n",[1107,8250,8252],{"class":1765,"line":8251},82,[1107,8253,8254],{},"    client.setServer(mqtt_server, 1883);\n",[1107,8256,8258],{"class":1765,"line":8257},83,[1107,8259,8260],{},"    client.setCallback(callback);\n",[1107,8262,8264],{"class":1765,"line":8263},84,[1107,8265,8016],{},[1107,8267,8269],{"class":1765,"line":8268},85,[1107,8270,1921],{"emptyLinePlaceholder":129},[1107,8272,8274],{"class":1765,"line":8273},86,[1107,8275,8276],{},"void loop() {\n",[1107,8278,8280],{"class":1765,"line":8279},87,[1107,8281,8282],{},"    if (!client.connected()) {\n",[1107,8284,8286],{"class":1765,"line":8285},88,[1107,8287,8288],{},"        reconnect();\n",[1107,8290,8292],{"class":1765,"line":8291},89,[1107,8293,7987],{},[1107,8295,8297],{"class":1765,"line":8296},90,[1107,8298,8299],{},"    client.loop();\n",[1107,8301,8303],{"class":1765,"line":8302},91,[1107,8304,8016],{},[271,8306,8307,8310,8313],{"start":107},[42,8308,8309],{},"Replace the placeholder values in the code: SSID (your Wi-Fi network's SSID), Wi-Fi Password (your Wi-Fi network's password), MQTT Client ID (the MQTT client ID you generated for esp32), MQTT Username and Password (the MQTT credentials you created).",[42,8311,8312],{},"After you've made these changes, click \"Upload\" in the Arduino IDE to upload the code to your ESP32.",[42,8314,8315],{},"Once the upload is complete, open the Serial Monitor (set the baud rate to 115200) to monitor the output.",[15,8317,8318],{},"If everything is set up correctly, you should see the output in the Serial Monitor as shown in the image.",[15,8320,8321,8325],{},[165,8322],{"alt":8323,"dataZoomable":103,"src":8324},"Serial monitor displaying the result when everything is set up correctly.","\u002Fblog\u002F2024\u002F11\u002Fimages\u002Fserial-monitor.png",[170,8326,8327],{},"Serial monitor displays the result when everything is set up correctly.",[15,8329,8330],{},"Once you verify the setup, you can unplug the USB from the computer and connect the ESP32 to a power adapter. With this, your ESP32 is now powered and connected to Wi-Fi (make sure your device is on the same Wi-Fi network as the one configured in the code), allowing you to control the LED from anywhere in the world via the MQTT commands sent through Node-RED.",[399,8332,7399],{"id":7398},[271,8334,8335,8350],{},[42,8336,8337,8340],{},[19,8338,8339],{},"Can't Upload Code to ESP32",[39,8341,8342],{},[42,8343,8344,8345,8349],{},"Solution: Make sure the correct board and port are selected in the Arduino IDE.\nCheck Tools > Board for the right ESP32 model and Tools > Port for the correct connection.\nIf the port is missing, ",[63,8346,7500],{"href":8347,"rel":8348},"https:\u002F\u002Fwww.silabs.com\u002Fdeveloper-tools\u002Fusb-to-uart-bridge-vcp-drivers?tab=downloads",[67]," and reinstall the CP210x USB drivers.",[42,8351,8352],{},[19,8353,8354],{},"ESP32 Keeps Disconnecting from MQTT",[39,8356,8357],{},[42,8358,8359],{},"Solution: Make sure both the ESP32 and Node-RED have unique MQTT client IDs.\nIf both devices share the same client ID, they will conflict and cause disconnections.",[271,8361,8362],{"start":294},[42,8363,8364,8367],{},[19,8365,8366],{},"ESP32 Doesn’t Respond to Commands (LED Not Turning On\u002FOff)",[39,8368,8369],{},[42,8370,8371],{},"Solution: Verify the topic in the ESP32 code matches the one in Node-RED (e.g., \u002FLedControl). If it still doesn't work, try rebooting your ESP32.",[31,8373,1425],{"id":1424},[15,8375,8376],{},"In this tutorial, we successfully connected the ESP32 to Node-RED using MQTT, enabling remote control of an LED via a FlowFuse dashboard. This simple IoT setup demonstrates how easy it is to interact with devices using MQTT and Node-RED, offering a flexible and scalable solution for future projects. With the ESP32, Node-RED, and FlowFuse, you can easily expand and integrate more devices into your IoT system.",[2582,8378,8379],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":8381},[8382,8383,8390],{"id":819,"depth":107,"text":820},{"id":7523,"depth":107,"text":7524,"children":8384},[8385,8386,8387,8388,8389],{"id":7530,"depth":294,"text":7531},{"id":7547,"depth":294,"text":7548},{"id":7616,"depth":294,"text":7617},{"id":7747,"depth":294,"text":7748},{"id":7398,"depth":294,"text":7399},{"id":1424,"depth":107,"text":1425},{"type":8392,"title":8393,"description":8394},"signup","Build and Scale Your IoT Projects with FlowFuse","Going from one ESP32 to a fleet of devices? FlowFuse gives you a managed MQTT broker, hosted Node-RED, remote device management, and dashboards in one platform, so you can deploy, monitor, and update your IoT devices from anywhere without managing infrastructure. Start free today.","2024-11-14","Learn how to connect an ESP32 to Node-RED using MQTT in 2026. Step-by-step guide covering the MQTT broker setup, Arduino IDE code, and building a dashboard to control your IoT device remotely, ideal for beginners and IoT hobbyists.","\u002Fblog\u002F2024\u002F11\u002Fimages\u002Fesp32-with-node-red.png",{"keywords":8399,"excerpt":8400},"esp32 with node red, node red esp32, connect esp32 to node red, esp node red, esp32 mqtt node red, esp32 node red, esp32 node red mqtt, mqtt esp32 node red, mqtt node red esp32, node red mqtt esp32, node red with esp32",{"type":12,"value":8401},[8402],[15,8403,7468],{},"\u002Fblog\u002F2024\u002F11\u002Fesp32-with-node-red",{"title":7462,"description":8396},{"loc":8404},"blog\u002F2024\u002F11\u002Fesp32-with-node-red","A step-by-step guide to controlling and monitoring an ESP32 over MQTT, with a FlowFuse dashboard.",[138,7457,8410],"mqtt","This guide explains how to connect an ESP32 to Node-RED using MQTT. The two devices don't talk directly, they exchange messages through an MQTT broker. Using FlowFuse's managed broker (broker.flowfuse.cloud), you create one MQTT client for Node-RED and one for the ESP32, build a Node-RED flow and FlowFuse dashboard that publishes commands (for example to \u002FLedControl), and program the ESP32 in the Arduino IDE with the PubSubClient library to subscribe to that topic and act on the payload, turning an LED on or off. Because everything routes through the broker, you can control the device from anywhere with Wi-Fi, and the same pattern works in reverse to read sensor data back into Node-RED.","X1ipdpj0DIsS_ivAQw8hXDwYq9oNghuNi4EvuQxUrr8",{"id":8414,"title":8415,"authors":8416,"body":8418,"cta":3,"date":8635,"description":8636,"extension":116,"image":8637,"lastUpdated":3,"meta":8638,"navigation":129,"path":8645,"seo":8646,"sitemap":8647,"stem":8648,"subtitle":8636,"tags":8649,"tldr":3,"video":3,"__hash__":8651},"blog\u002Fblog\u002F2024\u002F10\u002Fannouncement-mqtt-broker.md","MQTT Service Now Available on FlowFuse",[8417],"joe-pavitt",{"type":12,"value":8419,"toc":8628},[8420,8428,8437,8440,8444,8447,8469,8473,8476,8496,8499,8501,8509,8517,8525,8536,8539,8542,8551,8554,8563,8567,8573,8576,8586,8594,8601,8604,8625],[15,8421,8422,8423,8427],{},"In our ",[63,8424,8426],{"href":8425},"\u002Fblog\u002F2024\u002F10\u002Fflowfuse-release-2-10","recent product update"," we have added our very own MQTT service, built-in and ready to use alongside your Node-RED applications. We are always engaging with users and prospective customers and this has been a highly requested feature, and so we are delighted to announce that this is now live on FlowFuse Cloud for our Pro and Enterprise teams.",[15,8429,8430,8431,8436],{},"The MQTT Service is available now on ",[63,8432,8435],{"href":8433,"rel":8434},"https:\u002F\u002Fflowfuse.com\u002Fplatform\u002Fcloud\u002F",[67],"FlowFuse Cloud",". FlowFuse permits you to setup your own secure clients to begin publishing and subscribing to your own topics.",[15,8438,8439],{},"You can now use FlowFuse to manage your own MQTT Clients alongside your Node-RED instances, making it easier to build full-stack, event-driven applications within FlowFuse.",[31,8441,8443],{"id":8442},"use-cases","Use Cases",[15,8445,8446],{},"FlowFuse MQTT Service simplifies access to real-time data, an important element in optimizing industrial processes. With the power of FlowFuse, Node-RED and the MQTT service, your integrations are now even easier. Here are some typical uses cases:",[39,8448,8449,8463,8466],{},[42,8450,8451,8452],{},"Connect your MQTT enabled PLCs (like Omron N Series, Siemens S7, etc) to your Node-RED instance to open up such possibilities like:\n",[39,8453,8454,8457,8460],{},[42,8455,8456],{},"Data collection and analysis, Predictive Maintenance, OEE, Condition Based Monitoring.",[42,8458,8459],{},"Triggering actions like sending Emails or alerting your engineers about an event.",[42,8461,8462],{},"Realtime production monitoring of your facility.",[42,8464,8465],{},"Make use of modern IIoT devices on legacy systems by bridging the gap with MQTT.",[42,8467,8468],{},"Connect disparate systems together where Node-RED and FlowFuse act as the central hub for data processing and routing. This gives you the advantage of transforming data on the fly, applying extra contextual data from other systems, apply routing rules, and much more.",[31,8470,8472],{"id":8471},"pricing","Pricing",[15,8474,8475],{},"If you're on the Pro or Enterprise tiers of FlowFuse Cloud, then you don't have to pay any extra to get started with the MQTT Service.",[39,8477,8478,8488],{},[42,8479,8480,8483,8484,8487],{},[19,8481,8482],{},"Pro Tier:"," Includes ",[19,8485,8486],{},"5 clients for free"," as part of your existing plan",[42,8489,8490,8483,8493,8487],{},[19,8491,8492],{},"Enterprise Tier:",[19,8494,8495],{},"20 clients for free",[15,8497,8498],{},"In the near future we'll be publishing extra packages of clients that you can add to your team, beyond the amounts included with the base tiers.",[31,8500,880],{"id":879},[15,8502,8503,8504,8508],{},"To get started with your own MQTT Clients, navigate to ",[63,8505,8435],{"href":8506,"rel":8507},"https:\u002F\u002Fapp.flowfuse.com",[67],", and Sign In.",[271,8510,8511,8514],{},[42,8512,8513],{},"Click the new \"Broker\" option in the left navigation menu",[42,8515,8516],{},"Click \"Create Client\"",[15,8518,8519,8523],{},[165,8520],{"alt":8521,"dataZoomable":103,"src":8522},"Screenshot of FlowFuse's \"Create Client\" interface","\u002Fblog\u002F2024\u002F10\u002Fimages\u002Fmqtt-broker-add-client.png",[170,8524,8521],{},[271,8526,8527,8530,8533],{"start":294},[42,8528,8529],{},"Fill out the client's credentials (Username + Password)",[42,8531,8532],{},"Define the \"Access Control Rules\" (see more below)",[42,8534,8535],{},"Click \"Confirm\"",[15,8537,8538],{},"With your client created, you can then, in Node-RED, use the MQTT nodes to connect to your new client.",[15,8540,8541],{},"Setting up a new broker in Node-RED, you can use your credentials accordingly:",[15,8543,8544,8548,8549],{},[165,8545],{"alt":8546,"dataZoomable":103,"src":8547},"Screenshot of the MQTT Config, \"Connection\" tab in the Node-RED Editor","\u002Fblog\u002F2024\u002F10\u002Fimages\u002Fmqtt-broker-config.png","{width=400}\n",[170,8550,8546],{},[15,8552,8553],{},"And the respective \"Security\" tab:",[15,8555,8556,8560,8561],{},[165,8557],{"alt":8558,"dataZoomable":103,"src":8559},"Screenshot of the MQTT Config, \"Security\" tab in the Node-RED Editor","\u002Fblog\u002F2024\u002F10\u002Fimages\u002Fmqtt-broker-security.png","{width=440}\n",[170,8562,8558],{},[399,8564,8566],{"id":8565},"access-control-rules","Access Control Rules",[15,8568,8569,8570,29],{},"In MQTT, you can publish and subscribe to ",[170,8571,8572],{},"topics",[15,8574,8575],{},"These topics are strings that you can use to organise your data. In the Access Control Rules, you can define which topics your client can publish and subscribe to.",[15,8577,8578,8579,853,8582,8585],{},"For example, in a flow deployed to many PLCs on your factory floor, you might be publishing to the topics ",[924,8580,8581],{},"factory\u002Fbody-shop\u002Fplc\u002F1",[924,8583,8584],{},"factory\u002Fbody-shop\u002Fplc\u002F2",", etc.",[15,8587,8588,8592],{},[165,8589],{"alt":8590,"dataZoomable":103,"src":8591},"Simplified example of MQTT data flow for a Factory Floor","\u002Fblog\u002F2024\u002F10\u002Fimages\u002Fmqtt-factory-architecture.jpg",[170,8593,8590],{},[15,8595,8596,8597,8600],{},"Then, in a Cloud-Hosted flow, you can subscribe to ",[924,8598,8599],{},"factory\u002Fbody-shop\u002Fplc\u002F#"," to receive all messages from all PLCs in the body shop, and display relevant data into a Dashboard.",[15,8602,8603],{},"Each client that you create can be constrained in two ways:",[39,8605,8606,8619],{},[42,8607,8608,8611,8612,8615,8616,8618],{},[19,8609,8610],{},"Action",": You can limit clients to whether than can ",[170,8613,8614],{},"only"," subscribe, ",[170,8617,8614],{}," publish, or conduct both actions.",[42,8620,8621,8624],{},[19,8622,8623],{},"Topic",": You can control which topics a given client can interact with.",[15,8626,8627],{},"These constraints are particularly useful to ensure security throughout your MQTT network, and to ensure that data is only being sent and received by the correct components.",{"title":103,"searchDepth":104,"depth":104,"links":8629},[8630,8631,8632],{"id":8442,"depth":107,"text":8443},{"id":8471,"depth":107,"text":8472},{"id":879,"depth":107,"text":880,"children":8633},[8634],{"id":8565,"depth":294,"text":8566},"2024-10-31","We are thrilled to announce a significant milestone for FlowFuse, we now offer our very own MQTT service, built-in and ready to use with your Node-RED applications.","\u002Fblog\u002F2024\u002F10\u002Fimages\u002Fff-mqtt.png",{"excerpt":8639},{"type":12,"value":8640},[8641],[15,8642,8422,8643,8427],{},[63,8644,8426],{"href":8425},"\u002Fblog\u002F2024\u002F10\u002Fannouncement-mqtt-broker",{"title":8415,"description":8636},{"loc":8645},"blog\u002F2024\u002F10\u002Fannouncement-mqtt-broker",[8650,136,10,138,8410],"posts","WXUOFi01v2jpXLWIrgNeEbl-UCyv-kViaja0zKXX3ag",{"id":8653,"title":8654,"authors":8655,"body":8656,"cta":9233,"date":9236,"description":9237,"extension":116,"image":9238,"lastUpdated":6064,"meta":9239,"navigation":129,"path":9245,"seo":9246,"sitemap":9247,"stem":9248,"subtitle":9249,"tags":9250,"tldr":9251,"video":3,"__hash__":9252},"blog\u002Fblog\u002F2024\u002F09\u002Fhow-to-scrape-web-data-with-node-red.md","How to Scrape Data from Websites Using Node-RED",[144],{"type":12,"value":8657,"toc":9219},[8658,8661,8665,8668,8671,8675,8678,8681,8685,8691,8695,8698,8741,8747,8751,8754,8758,8761,8769,8773,8813,8822,8826,8835,9139,9147,9195,9204,9208,9211,9213,9216],[15,8659,8660],{},"Web scraping has become an indispensable tool for monitoring news, tracking competitors, and gathering insights. In this guide, you'll learn how to harness the power of Node-RED for efficient web scraping, allowing you to extract and manage data from various websites with ease that are not exposed through an API.",[31,8662,8664],{"id":8663},"what-is-web-scraping","What is Web Scraping?",[15,8666,8667],{},"Web scraping is a technique for automatically extracting data from websites. Instead of manually copying information from web pages, web scraping uses tools or scripts to access and retrieve data from the Internet efficiently. This process allows you to quickly gather large volumes of information, which is helpful for tasks such as tracking market trends, aggregating news, or collecting product details. By automating data collection, web scraping helps save time and reduce human error. It enables users to extract and analyze structured data from various sources, making it easier to compile and utilize information for research, business intelligence, or other purposes.",[15,8669,8670],{},"Web scraping can be helpful when APIs are unavailable or do not meet your requirements. It allows you to collect data directly from web pages, which can be beneficial for tasks like competitive analysis, market research, or tracking specific online content.",[31,8672,8674],{"id":8673},"how-does-web-scraping-works","How Does Web Scraping Works?",[15,8676,8677],{},"Web scraping involves systematically extracting data from websites using automated tools or scripts. The process begins with requesting a specific webpage. The response from the server is the HTML content of the page. This HTML code contains the structured information displayed on the webpage, organized in a format that describes the layout and content.",[15,8679,8680],{},"Once the HTML is received, the next step is parsing it. Parsing involves analyzing the HTML structure to identify and extract the data of interest. This may include navigating through nested elements, locating specific tags, and using selectors to target precise content such as text blocks, images, or links. The extracted data is then processed and stored in a format that suits the user's needs, whether a database, a CSV file, or another format suitable for analysis.",[31,8682,8684],{"id":8683},"web-scrapping-with-node-red","Web scrapping with Node-RED",[15,8686,8687,8688],{},"In this section, we will guide you through the process of scraping data from publicly available websites using Node-RED and demonstrate how to extract data from a website specifically designed for scraping practice. For this example, we will scrape country data from the page at ",[924,8689,8690],{},"https:\u002F\u002Fwww.scrapethissite.com\u002Fpages\u002Fsimple\u002F.",[399,8692,8694],{"id":8693},"sending-requests-to-a-webpage","Sending Requests to a Webpage",[15,8696,8697],{},"To start scraping data, follow these steps to send an HTTP GET request to the webpage:",[271,8699,8700,8705,8719,8723,8736],{},[42,8701,5054,8702,8704],{},[19,8703,1588],{}," node onto the canvas. This node allows you to manually trigger the HTTP request or set it to fire at specific intervals.",[42,8706,5054,8707,8709,8710,1242,8712,8715,8716,1279],{},[19,8708,1628],{}," node onto the canvas. Double-click it to configure and set the ",[19,8711,1650],{},[924,8713,8714],{},"GET."," Enter the webpage URL you want to scrape (e.g., ",[924,8717,8718],{},"https:\u002F\u002Fwww.scrapethissite.com\u002Fpages\u002Fsimple\u002F",[42,8720,5054,8721,1216],{},[19,8722,1634],{},[42,8724,8725,8726,8728,8729,8731,8732,8728,8734,1318],{},"Connect the ",[19,8727,1588],{}," node's output to the input of the ",[19,8730,1628],{}," node and the ",[19,8733,1628],{},[19,8735,1634],{},[42,8737,297,8738,8740],{},[19,8739,1330],{}," to save and deploy your flow.",[15,8742,8743,8744,8746],{},"Once deployed, click the ",[19,8745,1588],{}," button. You will see the raw HTML printed in the debug panel.",[399,8748,8750],{"id":8749},"parsing-and-extracting-data-from-html","Parsing and Extracting Data from HTML",[15,8752,8753],{},"Next, we need to process the raw HTML to extract meaningful data. This involves parsing the HTML content and identifying the specific information you want. To do this, first analyze the HTML structure of the webpage by opening the browser’s developer tools (press Ctrl + I or F12) and inspecting the elements to locate where the data is and in which HTML elements it resides.",[1165,8755,8757],{"id":8756},"analyzing-html-structure","Analyzing HTML Structure",[15,8759,8760],{},"Begin by analyzing the HTML structure of the webpage. Open your browser’s developer tools (press Ctrl + Shift + c ) and examine the elements to locate where the data resides and which HTML elements contain it. For example, on a page with a list of countries, each with its capital, population, and area, click on one of those countires elements to navigate to its HTML in the developer tools. Identify the selector that can be used to select those elements. On this webpage, the information about countries is contained within an element with the .countries class. You can use this class to extract all the data for the countries.",[15,8762,8763,8767],{},[165,8764],{"alt":8765,"dataZoomable":103,"src":8766},"Image showing the structure of the page and the data which we needed to extract","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fhtml-structer-of-target-website.png",[170,8768,8765],{},[1165,8770,8772],{"id":8771},"using-node-red-to-extract-data","Using Node-RED to extract data",[271,8774,8775,8780,8789,8792,8796,8809],{},[42,8776,5054,8777,1216],{},[19,8778,8779],{},"html",[42,8781,1641,8782,8784,8785,8788],{},[19,8783,8779],{}," node and enter the selector ",[924,8786,8787],{},".countries"," into the \"Selector\" field.",[42,8790,8791],{},"Set the output to \"only the text of element\" and keep other settings default.",[42,8793,5054,8794,1216],{},[19,8795,1634],{},[42,8797,8798,8799,8801,8802,8804,8805,8801,8807,1318],{},"Connect the output of the ",[19,8800,1628],{}," node to the input of the ",[19,8803,8779],{}," node and the output of the ",[19,8806,8779],{},[19,8808,1634],{},[42,8810,297,8811,8740],{},[19,8812,1330],{},[15,8814,8815,8816,8818,8819,8821],{},"When you click the ",[19,8817,1588],{}," button, you will see the array containing the text content from each ",[924,8820,8787],{}," div. While this data is a good starting point, it has yet to be in a format that is directly useful for analysis. To make the data more helpful, you'll need to transform it into objects with meaningful properties.",[399,8823,8825],{"id":8824},"transforming-data-into-structured-objects","Transforming Data into Structured Objects",[15,8827,8828,8829,8834],{},"You can use JavaScript in a Node-RED function node to transform data into structured objects. If you are familiar with JavaScript, this process will be straightforward. However, if you are not, you can use FlowFuse Expert to generate the necessary function. For more details, refer to our ",[63,8830,8833],{"href":8831,"rel":8832},"https:\u002F\u002Fwww.linkedin.com\u002Fposts\u002Fflowfuse_flowfuse-nodered-automation-activity-7226171132796637184-vKKt\u002F?utm_source=share&utm_medium=member_desktop",[67],"LinkedIn Post"," for a quick guide. However, in this section, we will use a low-code approach to transform the data.",[271,8836,8837,8850,8889,8929,8958,8990,9020,9043,9130],{},[42,8838,1263,8839,8842,8843,8846,8847,8849],{},[19,8840,8841],{},"Split"," node onto the canvas and connect it to the ",[19,8844,8845],{},"HTML"," node. This ",[19,8848,8841],{}," node will split the input array into individual string messages.",[42,8851,1263,8852,8842,8854,5091,8856,8859,8860],{},[19,8853,1251],{},[19,8855,8841],{},[924,8857,8858],{},"msg.name"," to the following JSONata expression to extract the country name:",[1756,8861,8863],{"className":5097,"code":8862,"language":5099,"meta":103,"style":103},"$trim($split(payload, \"Capital: \")[0])\n",[924,8864,8865],{"__ignoreMap":103},[1107,8866,8867,8870,8872,8875,8877,8880,8883,8885,8887],{"class":1765,"line":1766},[1107,8868,8869],{"class":1779},"$trim($split(payload, ",[1107,8871,1970],{"class":1783},[1107,8873,8874],{"class":1966},"Capital: ",[1107,8876,1970],{"class":1783},[1107,8878,8879],{"class":1779},")",[1107,8881,8882],{"class":1783},"[",[1107,8884,1855],{"class":1854},[1107,8886,1858],{"class":1783},[1107,8888,5192],{"class":1779},[42,8890,1224,8891,8893,8894],{},[924,8892,1227],{}," to the following Jsonata expression that will extract the capital and population from the string:",[1756,8895,8897],{"className":5097,"code":8896,"language":5099,"meta":103,"style":103},"$split($split(payload, \"Capital: \")[1], \"Population: \")\n",[924,8898,8899],{"__ignoreMap":103},[1107,8900,8901,8904,8906,8908,8910,8912,8914,8916,8918,8920,8922,8925,8927],{"class":1765,"line":1766},[1107,8902,8903],{"class":1779},"$split($split(payload, ",[1107,8905,1970],{"class":1783},[1107,8907,8874],{"class":1966},[1107,8909,1970],{"class":1783},[1107,8911,8879],{"class":1779},[1107,8913,8882],{"class":1783},[1107,8915,4944],{"class":1854},[1107,8917,1858],{"class":1783},[1107,8919,853],{"class":1779},[1107,8921,1970],{"class":1783},[1107,8923,8924],{"class":1966},"Population: ",[1107,8926,1970],{"class":1783},[1107,8928,5192],{"class":1779},[42,8930,2204,8931,8933,8934,5091,8936,8939,8940],{},[19,8932,1251],{}," node onto the canvas and connect it to the previous ",[19,8935,1251],{},[924,8937,8938],{},"msg.capital"," to the following Jsonata expression to trim and extract the value of the capital from the previously split data array:",[1756,8941,8943],{"className":5097,"code":8942,"language":5099,"meta":103,"style":103},"$trim(payload[0])\n",[924,8944,8945],{"__ignoreMap":103},[1107,8946,8947,8950,8952,8954,8956],{"class":1765,"line":1766},[1107,8948,8949],{"class":1779},"$trim(payload",[1107,8951,8882],{"class":1783},[1107,8953,1855],{"class":1854},[1107,8955,1858],{"class":1783},[1107,8957,5192],{"class":1779},[42,8959,1224,8960,8962,8963],{},[924,8961,1227],{}," to the following Jsonata expression to split the remaining string for area extraction:",[1756,8964,8966],{"className":5097,"code":8965,"language":5099,"meta":103,"style":103},"$split(payload[1], \"Area (km2): \")\n",[924,8967,8968],{"__ignoreMap":103},[1107,8969,8970,8973,8975,8977,8979,8981,8983,8986,8988],{"class":1765,"line":1766},[1107,8971,8972],{"class":1779},"$split(payload",[1107,8974,8882],{"class":1783},[1107,8976,4944],{"class":1854},[1107,8978,1858],{"class":1783},[1107,8980,853],{"class":1779},[1107,8982,1970],{"class":1783},[1107,8984,8985],{"class":1966},"Area (km2): ",[1107,8987,1970],{"class":1783},[1107,8989,5192],{"class":1779},[42,8991,2204,8992,8842,8994,8996,8997,9000,9001],{},[19,8993,1251],{},[19,8995,1251],{}," node from the previous step. Set ",[924,8998,8999],{},"msg.population"," to the following Jsonata expression to trim and convert the population value to a number:",[1756,9002,9004],{"className":5097,"code":9003,"language":5099,"meta":103,"style":103},"$number($trim(payload[0]))\n",[924,9005,9006],{"__ignoreMap":103},[1107,9007,9008,9011,9013,9015,9017],{"class":1765,"line":1766},[1107,9009,9010],{"class":1779},"$number($trim(payload",[1107,9012,8882],{"class":1783},[1107,9014,1855],{"class":1854},[1107,9016,1858],{"class":1783},[1107,9018,9019],{"class":1779},"))\n",[42,9021,1224,9022,9025,9026],{},[924,9023,9024],{},"msg.area"," to the following Jsonata expression to trim and convert the area value to a number:",[1756,9027,9029],{"className":5097,"code":9028,"language":5099,"meta":103,"style":103},"$number($trim(payload[1]))\n",[924,9030,9031],{"__ignoreMap":103},[1107,9032,9033,9035,9037,9039,9041],{"class":1765,"line":1766},[1107,9034,9010],{"class":1779},[1107,9036,8882],{"class":1783},[1107,9038,4944],{"class":1854},[1107,9040,1858],{"class":1783},[1107,9042,9019],{"class":1779},[42,9044,2204,9045,9047,9048,5091,9050,9052,9053],{},[19,9046,1251],{}," node onto the canvas and connect it to the last ",[19,9049,1251],{},[924,9051,1227],{}," to the following JSON object:",[1756,9054,9056],{"className":5097,"code":9055,"language":5099,"meta":103,"style":103},"{\n  \"name\": name,\n  \"capital\": capital,\n  \"population\": population,\n  \"area\": area\n}\n",[924,9057,9058,9063,9080,9096,9112,9126],{"__ignoreMap":103},[1107,9059,9060],{"class":1765,"line":1766},[1107,9061,9062],{"class":1783},"{\n",[1107,9064,9065,9068,9070,9072,9074,9077],{"class":1765,"line":107},[1107,9066,9067],{"class":1783},"  \"",[1107,9069,1914],{"class":1775},[1107,9071,1970],{"class":1783},[1107,9073,992],{"class":1783},[1107,9075,9076],{"class":1779}," name",[1107,9078,9079],{"class":1783},",\n",[1107,9081,9082,9084,9087,9089,9091,9094],{"class":1765,"line":294},[1107,9083,9067],{"class":1783},[1107,9085,9086],{"class":1775},"capital",[1107,9088,1970],{"class":1783},[1107,9090,992],{"class":1783},[1107,9092,9093],{"class":1779}," capital",[1107,9095,9079],{"class":1783},[1107,9097,9098,9100,9103,9105,9107,9110],{"class":1765,"line":104},[1107,9099,9067],{"class":1783},[1107,9101,9102],{"class":1775},"population",[1107,9104,1970],{"class":1783},[1107,9106,992],{"class":1783},[1107,9108,9109],{"class":1779}," population",[1107,9111,9079],{"class":1783},[1107,9113,9114,9116,9119,9121,9123],{"class":1765,"line":1868},[1107,9115,9067],{"class":1783},[1107,9117,9118],{"class":1775},"area",[1107,9120,1970],{"class":1783},[1107,9122,992],{"class":1783},[1107,9124,9125],{"class":1779}," area\n",[1107,9127,9128],{"class":1765,"line":1896},[1107,9129,8016],{"class":1783},[42,9131,9132,9133,8933,9136,9138],{},"Finally, drag a ",[19,9134,9135],{},"Join",[19,9137,1251],{}," node. This Join node will create an array of the objects we have created.",[15,9140,9141,9142,29],{},"When you click the inject button again, you will see that the data is now structured and formatted. The output will contain objects with properties such as name, capital, population, and area. This data can now be displayed on the FlowFuse dashboard table. For more details, refer to the ",[63,9143,9146],{"href":9144,"rel":9145},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-table.html",[67],"FlowFuse table widget",[15,9148,4917,9149,1150],{},[1107,9150,9151,9152,9154,9155,9160,9161,9170,9171,9180,9181,9190,9191,9194],{},"{\"id\":\"cc3c919ad9f93cc6\",\"type\":\"inject\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"props\":",[1107,9153,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":280,\"y\":200,\"wires\":[[\"43ba04d623a8aa57\"]]},{\"id\":\"2d66b9fa2858cf5f\",\"type\":\"html\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"property\":\"payload\",\"outproperty\":\"payload\",\"tag\":\".country\",\"ret\":\"text\",\"as\":\"single\",\"x\":620,\"y\":200,\"wires\":[[\"c4e9a4c8.487e68\"]]},{\"id\":\"43ba04d623a8aa57\",\"type\":\"http request\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"txt\",\"paytoqs\":\"ignore\",\"url\":\"",[63,9156,9159],{"href":9157,"rel":9158},"https:\u002F\u002Fwww.scrapethissite.com\u002Fpages\u002Fsimple\u002F%22,%22tls%22:%22%22,%22persist%22:false,%22proxy%22:%22%22,%22insecureHTTPParser%22:false,%22authType%22:%22%22,%22senderr%22:false,%22headers%22:%5B%5D,%22x%22:450,%22y%22:200,%22wires%22:%5B%5B%222d66b9fa2858cf5f%22%5D%5D%7D,%7B%22id%22:%228e7b462a5b5a064e%22,%22type%22:%22ui-table%22,%22z%22:%22380e37fed72e6885%22,%22group%22:%220c48f8d560157d3c%22,%22name%22:%22%22,%22label%22:%22text%22,%22order%22:1,%22width%22:0,%22height%22:0,%22maxrows%22:0,%22passthru%22:false,%22autocols%22:true,%22showSearch%22:true,%22selectionType%22:%22none%22,%22columns%22:%5B%5D,%22mobileBreakpoint%22:%22sm%22,%22mobileBreakpointType%22:%22defaults%22,%22x%22:1870,%22y%22:200,%22wires%22:%5B%5B%5D%5D%7D,%7B%22id%22:%22c4e9a4c8.487e68%22,%22type%22:%22split%22,%22z%22:%22380e37fed72e6885%22,%22name%22:%22Split",[67],"https:\u002F\u002Fwww.scrapethissite.com\u002Fpages\u002Fsimple\u002F\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":[],\"x\":450,\"y\":200,\"wires\":[[\"2d66b9fa2858cf5f\"]]},{\"id\":\"8e7b462a5b5a064e\",\"type\":\"ui-table\",\"z\":\"380e37fed72e6885\",\"group\":\"0c48f8d560157d3c\",\"name\":\"\",\"label\":\"text\",\"order\":1,\"width\":0,\"height\":0,\"maxrows\":0,\"passthru\":false,\"autocols\":true,\"showSearch\":true,\"selectionType\":\"none\",\"columns\":[],\"mobileBreakpoint\":\"sm\",\"mobileBreakpointType\":\"defaults\",\"x\":1870,\"y\":200,\"wires\":[[]]},{\"id\":\"c4e9a4c8.487e68\",\"type\":\"split\",\"z\":\"380e37fed72e6885\",\"name\":\"Split"," Array\",\"splt\":\"\\n\",\"spltType\":\"str\",\"arraySplt\":\"1\",\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":810,\"y\":200,\"wires\":[[\"1a1e8a0a.8e7b06\"]]},{\"id\":\"1a1e8a0a.8e7b06\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"Extract Name\",\"rules\":",[1107,9162,9163,9164,9166,9167,9169],{},"{\"t\":\"set\",\"p\":\"name\",\"pt\":\"msg\",\"to\":\"$trim($split(payload, \"Capital: \")",[1107,9165,1855],{},")\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$split($split(payload, \"Capital: \")",[1107,9168,4944],{},", \"Population: \")\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1000,\"y\":200,\"wires\":[[\"cfdb7c1f.9234b\"]]},{\"id\":\"cfdb7c1f.9234b\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"Extract Capital & Population\",\"rules\":",[1107,9172,9173,9174,9176,9177,9179],{},"{\"t\":\"set\",\"p\":\"capital\",\"pt\":\"msg\",\"to\":\"$trim(payload",[1107,9175,1855],{},")\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$split(payload",[1107,9178,4944],{},", \"Area (km2): \")\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1240,\"y\":200,\"wires\":[[\"fb93f89b.b96138\"]]},{\"id\":\"fb93f89b.b96138\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"Extract Population & Area\",\"rules\":",[1107,9182,9183,9184,9186,9187,9189],{},"{\"t\":\"set\",\"p\":\"population\",\"pt\":\"msg\",\"to\":\"$number($trim(payload",[1107,9185,1855],{},"))\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"area\",\"pt\":\"msg\",\"to\":\"$number($trim(payload",[1107,9188,4944],{},"))\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"{   \"name\": name,   \"capital\": capital,   \"population\": population,   \"area\": area}\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1510,\"y\":200,\"wires\":[[\"342166ac8729e9d7\"]]},{\"id\":\"342166ac8729e9d7\",\"type\":\"join\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"mode\":\"auto\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\n\",\"joinerType\":\"str\",\"accumulate\":true,\"timeout\":\"\",\"count\":\"\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":1710,\"y\":200,\"wires\":[[\"8e7b462a5b5a064e\"]]},{\"id\":\"0c48f8d560157d3c\",\"type\":\"ui-group\",\"name\":\"My Group\",\"page\":\"d0def7a91d3b7aa1\",\"width\":\"12\",\"height\":\"1\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"d0def7a91d3b7aa1\",\"type\":\"ui-page\",\"name\":\"Page 1\",\"ui\":\"c385dfc590b1308d\",\"path\":\"\u002F1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"6be033291dd76b17\",\"order\":1,\"className\":\"\",\"visible\":false,\"disabled\":false},{\"id\":\"c385dfc590b1308d\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,9192,9193],{},"\"ui-notification\",\"ui-control\",\"ui-button\"",",\"showPathInSidebar\":false,\"showPageTitle\":false,\"navigationStyle\":\"temporary\",\"titleBarStyle\":\"default\"},{\"id\":\"6be033291dd76b17\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#202c34\",\"primary\":\"#202c34\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#ffffff\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\",\"density\":\"default\"}}",[15,9196,9197,9201],{},[165,9198],{"alt":9199,"dataZoomable":103,"src":9200},"Left side: Image showing the countries table we created on the FlowFuse dashboard. Right side: The original webpage with countries.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fwebscrapping-result.png",[170,9202,9203],{},"Left side: Image showing the table we created on the FlowFuse dashboard. Right side: The original webpage with countries.",[31,9205,9207],{"id":9206},"legal-and-ethical-considerations","Legal and Ethical Considerations",[15,9209,9210],{},"Web scraping can be a valuable tool for gathering data, but it's crucial to navigate the legal and ethical landscape responsibly. Adhere to websites' terms of service, respect intellectual property and data privacy laws, and avoid actions that could disrupt a site's operation or misuse the scraped data. By staying informed and adhering to best practices, you can harness the power of web scraping tools like Node-RED while remaining ethically and legally compliant.",[31,9212,1425],{"id":1424},[15,9214,9215],{},"You’ve now learned to use Node-RED for web scraping, from sending requests and parsing HTML to transforming data into practical formats. This approach streamlines data collection from websites, making it easier to manage and analyze information efficiently.",[2582,9217,9218],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":103,"searchDepth":104,"depth":104,"links":9220},[9221,9222,9223,9231,9232],{"id":8663,"depth":107,"text":8664},{"id":8673,"depth":107,"text":8674},{"id":8683,"depth":107,"text":8684,"children":9224},[9225,9226,9230],{"id":8693,"depth":294,"text":8694},{"id":8749,"depth":294,"text":8750,"children":9227},[9228,9229],{"id":8756,"depth":104,"text":8757},{"id":8771,"depth":104,"text":8772},{"id":8824,"depth":294,"text":8825},{"id":9206,"depth":107,"text":9207},{"id":1424,"depth":107,"text":1425},{"type":748,"title":9234,"description":9235},"Automate Data Collection With Node-RED","FlowFuse gives you a production-ready platform to build, deploy, and manage Node-RED flows, with team collaboration, version control, and centralized management across all your instances.","2024-09-16","Learn how to use Node-RED for web scraping to efficiently collect, extract, and manage data from websites. This step-by-step guide covers everything you need to know about creating automated web scrapers using Node-RED.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fwebscraping-with-node-red.png",{"keywords":9240,"excerpt":9241},"node red webscraping, websrcaping with node-red, node red https request, online web scraper free, online web scraper free, custom web scraper, automated web scraping, web scraping solutions, data scraper easy web scraping",{"type":12,"value":9242},[9243],[15,9244,8660],{},"\u002Fblog\u002F2024\u002F09\u002Fhow-to-scrape-web-data-with-node-red",{"title":8654,"description":9237},{"loc":9245},"blog\u002F2024\u002F09\u002Fhow-to-scrape-web-data-with-node-red","A step-by-step guide to leveraging Node-RED for efficient web scraping and automated data extraction.",[138,7457],"Node-RED can be used to scrape data from websites that don't expose an API, by sending HTTP GET requests and parsing the returned HTML. This guide walks through building a complete web scraper in Node-RED from sending requests and extracting data with CSS selectors to storing the results using a public scraping-practice site as the example.","3zQ6-ZhtM9sSWVoepXX35KtmOyFOQ7wm7mIK4O5fjKw",{"id":9254,"title":9255,"authors":9256,"body":9257,"cta":9817,"date":9820,"description":9821,"extension":116,"image":9822,"lastUpdated":6203,"meta":9823,"navigation":129,"path":9829,"seo":9830,"sitemap":9831,"stem":9832,"subtitle":9833,"tags":9834,"tldr":9835,"video":3,"__hash__":9836},"blog\u002Fblog\u002F2024\u002F09\u002Fhow-to-use-subflow-in-node-red.md","How to create and use Subflow in Node-RED (2026)",[144],{"type":12,"value":9258,"toc":9803},[9259,9262,9265,9269,9277,9280,9284,9287,9290,9334,9338,9346,9354,9362,9365,9369,9377,9385,9394,9402,9411,9419,9431,9439,9444,9448,9451,9467,9475,9486,9494,9498,9501,9519,9527,9542,9550,9554,9557,9568,9576,9579,9606,9610,9613,9624,9632,9646,9650,9653,9662,9670,9679,9682,9765,9768,9772,9798,9800],[15,9260,9261],{},"In traditional programming, managing complex and repetitive tasks can quickly lead to a tangled mess of code that’s hard to maintain and update. To tackle this issue, developers use libraries or modules, reusable chunks of code that help organize functionality, minimize duplication, and keep codebases clean and manageable.",[15,9263,9264],{},"Node-RED brings a similar solution to its visual programming environment with Subflows. Imagine Subflows as the visual counterpart to libraries. In this guide, we will explore what Subflows are, how to create them, and how to use them effectively to enhance your Node-RED experience.",[31,9266,9268],{"id":9267},"what-exactly-are-subflows","What Exactly Are Subflows?",[15,9270,9271,9275],{},[165,9272],{"alt":9273,"dataZoomable":103,"src":9274},"Image showing a Node-RED flow at the top selected for creating a Subflow, and the resulting Subflow at the bottom.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fsubflow.png",[170,9276,9273],{},[15,9278,9279],{},"Subflows in Node-RED are a way to group together a set of nodes and reusable flows into a single, reusable node. This helps you manage and organize complex workflows by encapsulating repetitive or complex logic into a modular unit. You can think of Subflows as custom nodes that you create and use within your flows to simplify your design and reduce redundancy.",[31,9281,9283],{"id":9282},"creating-a-subflow-in-node-red","Creating a Subflow in Node-RED",[15,9285,9286],{},"In this section, we will create a Subflow for a flow that sends requests to an API and returns results. If the request faces an issue, it retries until it reaches the maximum retry limit. Let's assume we need to use this flow in multiple places, for different APIs, each with different retry timeouts. To avoid duplicating the flow logic, we can create a Subflow.",[15,9288,9289],{},"To follow along, import the following flow into your Node-RED instance.",[15,9291,4917,9292,1150],{},[1107,9293,9294,9295,9297,9298,9300,9301,9303,9304,9309,9310,9313,9314,9317,9318,9321,9322,9325,9326,9329,9330,9333],{},"{\"id\":\"132f4fdc40d55e89\",\"type\":\"debug\",\"z\":\"380e37fed72e6885\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":2840,\"y\":280,\"wires\":",[1107,9296],{},"},{\"id\":\"612819f76617e5a8\",\"type\":\"debug\",\"z\":\"380e37fed72e6885\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":2840,\"y\":220,\"wires\":",[1107,9299],{},"},{\"id\":\"7e9e8e1af751bb92\",\"type\":\"inject\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"props\":",[1107,9302,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":1460,\"y\":240,\"wires\":[[\"8899fea497064b8c\"]]},{\"id\":\"b4eca1de14599dd1\",\"type\":\"delay\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"pauseType\":\"delayv\",\"timeout\":\"5\",\"timeoutUnits\":\"milliseconds\",\"rate\":\"1\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"1\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":1900,\"y\":240,\"wires\":[[\"96ca1ed69e7fa7ac\"]]},{\"id\":\"96ca1ed69e7fa7ac\",\"type\":\"http request\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"txt\",\"paytoqs\":\"ignore\",\"url\":\"",[63,9305,9308],{"href":9306,"rel":9307},"https:\u002F\u002Fjsonplaceholder.typicode.com\u002Ftodos%22,%22tls%22:%22%22,%22persist%22:false,%22proxy%22:%22%22,%22insecureHTTPParser%22:false,%22authType%22:%22%22,%22senderr%22:false,%22headers%22:%5B%5D,%22x%22:2050,%22y%22:240,%22wires%22:%5B%5B%22f523bb034db360a4%22%5D%5D%7D,%7B%22id%22:%223eb4ec6b71fc383b%22,%22type%22:%22switch%22,%22z%22:%22380e37fed72e6885%22,%22name%22:%22if",[67],"https:\u002F\u002Fjsonplaceholder.typicode.com\u002Ftodos\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":[],\"x\":2050,\"y\":240,\"wires\":[[\"f523bb034db360a4\"]]},{\"id\":\"3eb4ec6b71fc383b\",\"type\":\"switch\",\"z\":\"380e37fed72e6885\",\"name\":\"if"," success\",\"property\":\"statusCode\",\"propertyType\":\"msg\",\"rules\":",[1107,9311,9312],{},"{\"t\":\"btwn\",\"v\":\"200\",\"vt\":\"num\",\"v2\":\"299\",\"v2t\":\"num\"},{\"t\":\"else\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":2420,\"y\":240,\"wires\":[[\"612819f76617e5a8\"],",[1107,9315,9316],{},"\"bad4bdb7e00b7a80\"","]},{\"id\":\"bad4bdb7e00b7a80\",\"type\":\"switch\",\"z\":\"380e37fed72e6885\",\"name\":\"if max retries\",\"property\":\"retry_counter\",\"propertyType\":\"msg\",\"rules\":",[1107,9319,9320],{},"{\"t\":\"gte\",\"v\":\"10000\",\"vt\":\"num\"},{\"t\":\"else\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":2590,\"y\":280,\"wires\":[[\"132f4fdc40d55e89\"],",[1107,9323,9324],{},"\"b4eca1de14599dd1\"","]},{\"id\":\"8899fea497064b8c\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"rules\":",[1107,9327,9328],{},"{\"t\":\"set\",\"p\":\"delay\",\"pt\":\"msg\",\"to\":\"retry_interval\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"retry_counter\",\"pt\":\"msg\",\"to\":\"0\",\"tot\":\"num\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1720,\"y\":240,\"wires\":[[\"b4eca1de14599dd1\"]]},{\"id\":\"f523bb034db360a4\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"retry_counter++\",\"rules\":",[1107,9331,9332],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"retry_counter+1\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":2240,\"y\":240,\"wires\":[[\"3eb4ec6b71fc383b\"]]}",[399,9335,9337],{"id":9336},"creating-subflow-of-selection","Creating subflow of selection",[15,9339,9340,9344],{},[165,9341],{"alt":9342,"dataZoomable":103,"src":9343},"Image showing process of creating subflow from the selection","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fselecting-and-converting-subflow.gif",[170,9345,9342],{},[271,9347,9348,9351],{},[42,9349,9350],{},"Select the flow you want to convert into a Subflow.",[42,9352,9353],{},"Open the main menu by clicking the top-right menu icon, and select \"Selection to Subflow\" under the Subflows option.",[15,9355,9356,9360],{},[165,9357],{"alt":9358,"dataZoomable":103,"src":9359},"Image showing subflow node added in the node palette","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fsubflow-showing-in-pallete.png",[170,9361,9358],{},[15,9363,9364],{},"Once selected, the Subflow will be added to the node palette like other nodes. The selected flow will also be converted into a single node representing the Subflow.",[399,9366,9368],{"id":9367},"adding-properties-to-the-subflow","Adding Properties to the Subflow",[271,9370,9371],{},[42,9372,9373,9374,29],{},"Double-click on the Subflow, then click on ",[19,9375,9376],{},"\"Edit Subflow template\"",[15,9378,9379,9383],{},[165,9380],{"alt":9381,"dataZoomable":103,"src":9382},"Editing the Subflow template by clicking on the 'Edit Subflow Template' option.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fedit-template-option.png",[170,9384,9381],{},[271,9386,9387],{"start":107},[42,9388,9389,9390,9393],{},"A new flow tab for the Subflow will open. Click on ",[19,9391,9392],{},"\"Edit Properties\""," in the top-left corner.",[15,9395,9396,9400],{},[165,9397],{"alt":9398,"dataZoomable":103,"src":9399},"The edit properties button for a Subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fedit-properties.png",[170,9401,9398],{},[271,9403,9404],{"start":294},[42,9405,9406,9407,9410],{},"To add environment properties, click on the ",[19,9408,9409],{},"\"+ add\""," button located at the bottom-left.",[15,9412,9413,9417],{},[165,9414],{"alt":9415,"dataZoomable":103,"src":9416},"The 'Add' button for adding environment properties for subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fadd-env.png",[170,9418,9415],{},[271,9420,9421,9424],{"start":104},[42,9422,9423],{},"In the field that opens, give the property a name and set its default value.",[42,9425,9426,9427,9430],{},"Once you have added all your properties, you can view a preview by switching to the ",[19,9428,9429],{},"\"UI PREVIEW\""," tab.",[15,9432,9433,9437],{},[165,9434],{"alt":9435,"dataZoomable":103,"src":9436},"A preview of the Subflow environment properties","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fui-preivew-env.png",[170,9438,9435],{},[271,9440,9441],{"start":1896},[42,9442,9443],{},"Click \"Done\" to save.",[399,9445,9447],{"id":9446},"setting-added-environment-variables-in-the-nodes","Setting Added Environment Variables in the Nodes",[15,9449,9450],{},"Now that we have added properties for the Subflow (which are environment variables), we need to use them in the relevant nodes, such as the HTTP request node, which will require an API and the max-retry setting.",[271,9452,9453],{},[42,9454,9455,9456,9459,9460,9463,9464,9466],{},"Double-click on the ",[19,9457,9458],{},"HTTP request"," node, set the environment variable as ",[924,9461,9462],{},"${your_env_name}"," into the URL feild, and click ",[19,9465,2453],{}," to save.",[15,9468,9469,9473],{},[165,9470],{"alt":9471,"dataZoomable":103,"src":9472},"The URL field of an HTTP request node in Node-RED with an environment variable added.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fhttp-request-url-env-adding.png",[170,9474,9471],{},[271,9476,9477],{"start":107},[42,9478,9479,9480,9483,9484,9466],{},"Next, double-click on the ",[19,9481,9482],{},"switch"," node named \"if max retries,\" update the hardcoded max retry condition value to the environment variable you set for it, and click ",[19,9485,2453],{},[15,9487,9488,9492],{},[165,9489],{"alt":9490,"dataZoomable":103,"src":9491},"The switch node in Node-RED with a max retry condition set using an environment variable.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fmax-retry-setting.png",[170,9493,9490],{},[399,9495,9497],{"id":9496},"managing-subflow-input-and-output-ports","Managing Subflow Input and Output Ports",[15,9499,9500],{},"As we know, any node in Node-RED requires input and output ports to manage its data flow. Similarly, a Subflow node requires these ports to function correctly. In our Subflow example, it needs to be triggered and therefore requires at least one input port and one or more output ports. Specifically, our Subflow has two outputs: one for successfully fetched data and another to indicate when the maximum retry limit has been exceeded.",[271,9502,9503],{},[42,9504,1654,9505,9508,9509,9512,9513,9515,9516,1318],{},[19,9506,9507],{},"Subflow"," tab, at the top, you will see an option for ",[19,9510,9511],{},"inputs"," with values 0 and 1. Click on ",[19,9514,4944],{}," to add an input port (as a any Node-RED node can have only one input port). Once set to 1, you will see an input port added in the Subflow tab. Connect it to the appropriate node; in our example, it should be connected to the first ",[19,9517,9518],{},"change",[15,9520,9521,9525],{},[165,9522],{"alt":9523,"dataZoomable":103,"src":9524},"Option to add input port for subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Finput-adding-subflow.png",[170,9526,9523],{},[271,9528,9529],{"start":107},[42,9530,9531,9532,9534,9535,9538,9539,9541],{},"Next, right after the ",[19,9533,9511],{}," option, you will see an option for ",[19,9536,9537],{},"outputs",". Unlike inputs, you can add as many outputs as you need. Once you've added the outputs, connect them to the appropriate nodes. In our example, the first output should be connected to the first input of both ",[19,9540,9482],{}," nodes.",[15,9543,9544,9548],{},[165,9545],{"alt":9546,"dataZoomable":103,"src":9547},"Option to add output ports for subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Foutput-adding-subflow.png",[170,9549,9546],{},[399,9551,9553],{"id":9552},"adding-status-for-subflow-nodes","Adding Status for Subflow Nodes",[15,9555,9556],{},"To effectively manage and monitor the execution of Subflows, you can add status indicators to your Subflow nodes. This allows you to see if the Subflow is functioning correctly and helps in debugging if something goes wrong. To add a status indicator:",[271,9558,9559],{},[42,9560,9561,9562,9565,9566,29],{},"In the Subflow flow tab at the top, click on the ",[19,9563,9564],{},"Status"," node option to add a status node. This status node can be connected to the Node-RED status node to capture and display all statuses, or you can configure it to use ",[924,9567,1227],{},[15,9569,9570,9574],{},[165,9571],{"alt":9572,"dataZoomable":103,"src":9573},"Option to add status for subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fstatus-adding-subflow.png",[170,9575,9572],{},[15,9577,9578],{},"In our example, we need two indicators: one to display when the flow is retrying to request and another to indicate that the fetch operation has successfully completed.",[271,9580,9581],{"start":107},[42,9582,5082,9583,9585,9586,9589,9590,1242,9592,9595,9596,9599,9600,1242,9602,9605],{},[19,9584,1251],{}," nodes onto the Canvas. Connect one Change node to the ",[19,9587,9588],{},"if success"," switch node's first output and set the ",[924,9591,1227],{},[924,9593,9594],{},"\"completed\"",". Connect the other Change node to the ",[19,9597,9598],{},"if max retries"," switch node's first output and set its ",[924,9601,1227],{},[924,9603,9604],{},"\"retrying\"",". Then, connect both Change nodes to the input of the Subflow status node.",[399,9607,9609],{"id":9608},"customizing-the-appearance-of-a-subflow-node","Customizing the Appearance of a Subflow Node",[15,9611,9612],{},"Node-RED allows you to customize the appearance of Subflow nodes, including setting the color, icon, port labels, and selecting the category in which it will be visible in the node palette.",[271,9614,9615],{},[42,9616,9617,9618,9620,9621,9430],{},"In the Subflow flow tab, click on the ",[19,9619,9392],{}," option in the top-left corner and switch to the ",[19,9622,9623],{},"\"Appearance\"",[15,9625,9626,9630],{},[165,9627],{"alt":9628,"dataZoomable":103,"src":9629},"Image showing the apperance tab of subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fcustomizing-apperance.png",[170,9631,9628],{},[271,9633,9634,9640,9643],{"start":107},[42,9635,9636,9637,29],{},"Select a category from the available categories or add a new one by clicking on ",[19,9638,9639],{},"\"Add new\"",[42,9641,9642],{},"Choose a color for the Subflow node and select an icon.",[42,9644,9645],{},"Provide labels for the ports so that when someone hovers over the Subflow input or output ports, they can quickly understand their purpose.",[399,9647,9649],{"id":9648},"adding-documentation-for-a-subflow-node","Adding Documentation for a Subflow Node",[15,9651,9652],{},"Node-RED allows you to add documentation for Subflow nodes, providing guidance on how to use them. This documentation will be rendered in the help sidebar, similar to other nodes.",[271,9654,9655],{},[42,9656,9617,9657,9620,9659,9430],{},[19,9658,9392],{},[19,9660,9661],{},"\"Description\"",[15,9663,9664,9668],{},[165,9665],{"alt":9666,"dataZoomable":103,"src":9667},"Image showing the description tab of subflow","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fdocumentation-tab.png",[170,9669,9628],{},[271,9671,9672,9675],{"start":107},[42,9673,9674],{},"Enter the documentation content in markdown format that provides guidance on how to use the Subflow node effectively.",[42,9676,297,9677,9466],{},[19,9678,2453],{},[15,9680,9681],{},"Once saved, the documentation will be displayed in the help sidebar when users click on the Subflow node in the Node-RED palette or hover over and select the help option for that node.",[15,9683,4917,9684,1150],{},[1107,9685,9686,9687,9693,9694,9704,9705,9708,9709,9712,9713,9716,9717,9719,9720,9722,9723,9725,9726,9729,9730,9732,9733,9735,9736,9739,9740,9743,9744,9746,9747,9300,9749,9751,9752,9760,9761,9764],{},"{\"id\":\"ea5436b592a86b90\",\"type\":\"subflow\",\"name\":\"API Retry \",\"info\":\"## hee\",\"category\":\"function\",\"in\":",[1107,9688,9689,9690,4957],{},"{\"x\":50,\"y\":30,\"wires\":",[1107,9691,9692],{},"{\"id\":\"8899fea497064b8c\"}",",\"out\":",[1107,9695,9696,9697,9700,9701,4957],{},"{\"x\":1140,\"y\":60,\"wires\":",[1107,9698,9699],{},"{\"id\":\"3eb4ec6b71fc383b\",\"port\":0}","},{\"x\":1610,\"y\":120,\"wires\":",[1107,9702,9703],{},"{\"id\":\"092547737a0cbee2\",\"port\":0}",",\"env\":",[1107,9706,9707],{},"{\"name\":\"URL\",\"type\":\"str\",\"value\":\"\"},{\"name\":\"MAX_RETRY\",\"type\":\"str\",\"value\":\"\"}",",\"meta\":{},\"color\":\"#D7D7A0\",\"inputLabels\":",[1107,9710,9711],{},"\"Trigger\"",",\"outputLabels\":",[1107,9714,9715],{},"\"API Response\",\"Max Retry Exeeded\"",",\"icon\":\"node-red\u002Fwhite-globe.svg\"},{\"id\":\"b4eca1de14599dd1\",\"type\":\"delay\",\"z\":\"ea5436b592a86b90\",\"name\":\"\",\"pauseType\":\"delayv\",\"timeout\":\"5\",\"timeoutUnits\":\"milliseconds\",\"rate\":\"1\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"1\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":400,\"y\":80,\"wires\":[[\"96ca1ed69e7fa7ac\"]]},{\"id\":\"96ca1ed69e7fa7ac\",\"type\":\"http request\",\"z\":\"ea5436b592a86b90\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"txt\",\"paytoqs\":\"ignore\",\"url\":\"${URL}\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":",[1107,9718],{},",\"x\":550,\"y\":80,\"wires\":[[\"f523bb034db360a4\"]]},{\"id\":\"3eb4ec6b71fc383b\",\"type\":\"switch\",\"z\":\"ea5436b592a86b90\",\"name\":\"if success\",\"property\":\"statusCode\",\"propertyType\":\"msg\",\"rules\":",[1107,9721,9312],{},",\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":920,\"y\":80,\"wires\":[[],",[1107,9724,9316],{},"]},{\"id\":\"bad4bdb7e00b7a80\",\"type\":\"switch\",\"z\":\"ea5436b592a86b90\",\"name\":\"if max retries\",\"property\":\"retry_counter\",\"propertyType\":\"msg\",\"rules\":",[1107,9727,9728],{},"{\"t\":\"gte\",\"v\":\"MAX_RETRY\",\"vt\":\"env\"},{\"t\":\"else\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":1090,\"y\":120,\"wires\":[[\"092547737a0cbee2\"],",[1107,9731,9324],{},"]},{\"id\":\"8899fea497064b8c\",\"type\":\"change\",\"z\":\"ea5436b592a86b90\",\"name\":\"\",\"rules\":",[1107,9734,9328],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":220,\"y\":80,\"wires\":[[\"b4eca1de14599dd1\"]]},{\"id\":\"f523bb034db360a4\",\"type\":\"change\",\"z\":\"ea5436b592a86b90\",\"name\":\"retry_counter++\",\"rules\":",[1107,9737,9738],{},"{\"t\":\"set\",\"p\":\"retry_counter\",\"pt\":\"msg\",\"to\":\"retry_counter+1\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":740,\"y\":80,\"wires\":[[\"3eb4ec6b71fc383b\"]]},{\"id\":\"092547737a0cbee2\",\"type\":\"change\",\"z\":\"ea5436b592a86b90\",\"name\":\"\",\"rules\":",[1107,9741,9742],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"Max retry exeeded\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1360,\"y\":120,\"wires\":[[]]},{\"id\":\"132f4fdc40d55e89\",\"type\":\"debug\",\"z\":\"380e37fed72e6885\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":2420,\"y\":320,\"wires\":",[1107,9745],{},"},{\"id\":\"612819f76617e5a8\",\"type\":\"debug\",\"z\":\"380e37fed72e6885\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":2420,\"y\":200,\"wires\":",[1107,9748],{},[1107,9750,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":1840,\"y\":260,\"wires\":[[\"ccadc2ba40bb8606\"]]},{\"id\":\"ccadc2ba40bb8606\",\"type\":\"subflow:ea5436b592a86b90\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"env\":",[1107,9753,9754,9755],{},"{\"name\":\"URL\",\"value\":\"",[63,9756,9759],{"href":9757,"rel":9758},"https:\u002F\u002Fjsonplaceholder.typicode.com\u002FphotosS%22,%22type%22:%22str%22%7D,%7B%22name%22:%22MAX_RETRY%22,%22value%22:%2210000%22,%22type%22:%22str%22%7D",[67],"https:\u002F\u002Fjsonplaceholder.typicode.com\u002FphotosS\",\"type\":\"str\"},{\"name\":\"MAX_RETRY\",\"value\":\"10000\",\"type\":\"str\"}",",\"x\":2160,\"y\":260,\"wires\":[[\"612819f76617e5a8\"],",[1107,9762,9763],{},"\"132f4fdc40d55e89\"","]}",[15,9766,9767],{},"Now, just like regular Node-RED nodes, you can effectively use this Subflow node in your projects. With its added documentation, customized appearance, and status indicators, it integrates seamlessly into your Node-RED environment, enhancing both usability and functionality.",[399,9769,9771],{"id":9770},"benefits-of-using-subflows","Benefits of using subflows.",[39,9773,9774,9780,9786,9792],{},[42,9775,9776,9779],{},[19,9777,9778],{},"Modularity",": Subflows allow you to group related nodes into a single, reusable unit, making complex flows easier to manage.",[42,9781,9782,9785],{},[19,9783,9784],{},"Code Reuse",": They help avoid duplicating similar logic across different parts of your flow, saving time and effort.",[42,9787,9788,9791],{},[19,9789,9790],{},"Simplified Design",": Subflows can simplify your main flow by hiding complexity within a single node.",[42,9793,9794,9797],{},[19,9795,9796],{},"Easier Maintenance",": Updating a Subflow automatically updates all instances where it is used, making maintenance quicker.",[31,9799,1425],{"id":1424},[15,9801,9802],{},"In this guide, we explored the concept of subflows in Node-RED, including their definition and purpose. We walked through the steps to create and configure subflows, demonstrating how to integrate them into your main flow. Additionally, we discussed how to edit and update existing subflows, and provided best practices for managing and organizing them effectively.",{"title":103,"searchDepth":104,"depth":104,"links":9804},[9805,9806,9816],{"id":9267,"depth":107,"text":9268},{"id":9282,"depth":107,"text":9283,"children":9807},[9808,9809,9810,9811,9812,9813,9814,9815],{"id":9336,"depth":294,"text":9337},{"id":9367,"depth":294,"text":9368},{"id":9446,"depth":294,"text":9447},{"id":9496,"depth":294,"text":9497},{"id":9552,"depth":294,"text":9553},{"id":9608,"depth":294,"text":9609},{"id":9648,"depth":294,"text":9649},{"id":9770,"depth":294,"text":9771},{"id":1424,"depth":107,"text":1425},{"type":748,"title":9818,"description":9819},"Build and Reuse Node-RED Flows Across Your Team","FlowFuse gives you version control, team collaboration, and centralized deployment, so your subflows and reusable logic stay consistent across every instance and every engineer.","2024-09-13","Learn how to effectively use subflows in Node-RED with this comprehensive guide. Discover the benefits, creation steps, and best practices for managing subflows to streamline your automation workflows.","\u002Fblog\u002F2024\u002F09\u002Fimages\u002Fsubflow-in-node-red.png",{"keywords":9824,"excerpt":9825},"node red subflow, nodered subflow, node red subflow environment variables, node red create subflow, node red subflows",{"type":12,"value":9826},[9827],[15,9828,9261],{},"\u002Fblog\u002F2024\u002F09\u002Fhow-to-use-subflow-in-node-red",{"title":9255,"description":9821},{"loc":9829},"blog\u002F2024\u002F09\u002Fhow-to-use-subflow-in-node-red","A Practical Guide to Implementing Subflows in Node-RED for Efficient Workflow Management",[138,7457],"Subflows in Node-RED let you group reusable logic into a single custom node, similar to libraries or modules in traditional programming. This guide covers how to create a subflow from an existing flow, add configurable environment variable properties to it, and use it across multiple places in your Node-RED projects to eliminate duplication.","nq7gZCzbGMp6zPXcb9RBGPxHZpCpsLdwmgl6k-WJGfw",{"id":9838,"title":9839,"authors":9840,"body":9841,"cta":3,"date":11035,"description":11036,"extension":116,"image":3,"lastUpdated":11037,"meta":11038,"navigation":129,"path":11044,"seo":11045,"sitemap":11046,"stem":11047,"subtitle":11048,"tags":11049,"tldr":11050,"video":3,"__hash__":11051},"blog\u002Fblog\u002F2024\u002F08\u002Fusing-mqtt-sparkplugb-with-node-red.md","MQTT Sparkplug B Implementation: Protocol, Architecture & Best Practices",[144],{"type":12,"value":9842,"toc":11007},[9843,9846,9850,9869,9872,9876,9891,9894,9903,9910,9919,9923,9926,9930,9933,9936,9943,9947,9950,9953,9957,9974,9977,9981,9984,9987,9990,9993,9997,10008,10015,10019,10027,10031,10041,10051,10055,10058,10061,10065,10089,10093,10108,10111,10404,10408,10411,10417,10423,10426,10429,10431,10433,10451,10455,10486,10496,10500,10636,10647,10661,10670,10681,10687,10702,10706,10740,10748,10755,10759,10762,10772,10821,10824,10866,10877,10884,10904,10907,10924,10933,10937,10940,10993,10995,10998,11001,11004],[15,9844,9845],{},"Connected devices can generate a lot of data, but without a standardized format, managing and consuming it can be tricky. MQTT certainly simplifies getting your messages delivered but it does not enforce any structure. This is where MQTT Sparkplug B helps by providing a clear, standardized format for data. In this guide, we’ll show you how to use MQTT Sparkplug B with Node-RED to make managing your device data easier and more organized.",[31,9847,9849],{"id":9848},"what-is-mqtt-sparkplug","What is MQTT Sparkplug?",[15,9851,9852,9853,9858,9859,9863,9864,9868],{},"MQTT Sparkplug B is an open-source specification governed by the ",[63,9854,9857],{"href":9855,"rel":9856},"https:\u002F\u002Fwww.eclipse.org\u002Fprojects\u002Fefsp\u002F",[67],"Eclipse Foundation Specification Process (EFSP)",". It defines a standardized MQTT topic namespace and payload format specifically designed for Industrial IoT (IIoT), with particular focus on real-time ",[63,9860,9862],{"href":9861},"\u002Fsolutions\u002Fscada\u002F","SCADA",", control systems, and ",[63,9865,9867],{"href":9866},"\u002Fblog\u002F2025\u002F11\u002Fbuilding-hmi-for-equipment-control\u002F","HMI"," solutions.",[15,9870,9871],{},"At its core, Sparkplug B extends MQTT 3.1.1 by adding structured topic namespace conventions, Google Protocol Buffer encoded payloads, state-aware birth and death certificates, metric aliasing for bandwidth optimization, and store-and-forward capabilities for intermittent connectivity. These additions transform MQTT from a simple messaging protocol into a complete industrial communication framework.",[399,9873,9875],{"id":9874},"the-industrial-integration-problem","The Industrial Integration Problem",[15,9877,9878,9879,9882,9883,9886,9887,9890],{},"In typical factory environments, every machine manufacturer implements MQTT differently. Consider a real-world scenario where Machine A publishes temperature data to ",[924,9880,9881],{},"factory\u002Fline1\u002Ftemp"," with a JSON payload containing the temperature value. Meanwhile, Machine B sends its data to ",[924,9884,9885],{},"sensors\u002FmachineB\u002Fenv"," with a completely different JSON structure that includes both temperature and timestamp. Machine C takes yet another approach, publishing raw numeric values to ",[924,9888,9889],{},"data\u002Fmc\u002Fstatus"," with no context or metadata.",[15,9892,9893],{},"Each device requires custom parsing logic, error handling, and documentation. Integration complexity grows exponentially with each new device type. Your development team spends weeks building and maintaining custom parsers instead of focusing on business logic.",[15,9895,9896,9900],{},[165,9897],{"alt":9898,"src":9899},"Manufacturing dashboard struggling with non-standardized MQTT data from multiple sources","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fwith-plane-mqtt.png",[170,9901,9902],{},"Dashboard complexity increases exponentially without standardized data formats",[15,9904,9905,9906,9909],{},"With Sparkplug B standardization, all devices publish to structured topics following the format ",[924,9907,9908],{},"spBv1.0\u002FFactory\u002FDDATA\u002FLine1\u002FMachineA"," with consistent Protocol Buffer payloads. Each message contains typed metrics, timestamps, and quality indicators. Integration becomes predictable and maintainable. When you add a new device, it automatically describes its capabilities through birth certificates, eliminating the need for custom integration code.",[15,9911,9912,9916],{},[165,9913],{"alt":9914,"src":9915},"Manufacturing dashboard efficiently processing standardized Sparkplug B data","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fwith-sparkplug.png",[170,9917,9918],{},"Standardized format enables reliable data aggregation across heterogeneous devices",[31,9920,9922],{"id":9921},"sparkplug-b-architecture-patterns","Sparkplug B Architecture Patterns",[15,9924,9925],{},"Several architectural patterns have emerged from production Sparkplug B deployments. Understanding these patterns helps you design scalable, maintainable systems.",[399,9927,9929],{"id":9928},"edge-node-architecture","Edge Node Architecture",[15,9931,9932],{},"The edge node serves as the gateway between physical devices and the MQTT broker. A single edge node typically manages multiple devices, publishing aggregate birth certificates and handling communication for all attached devices. This centralized approach simplifies device management and reduces broker connections.",[15,9934,9935],{},"Edge nodes implement store-and-forward buffering to handle temporary connectivity loss. When the broker becomes unreachable, the edge node buffers data locally. After reconnection, it publishes buffered data with historical flags set, allowing applications to distinguish between real-time and historical data.",[15,9937,9938,9939,9942],{},"The edge node monitors broker connectivity and automatically publishes ",[924,9940,9941],{},"NBIRTH"," after reconnection. It manages sequence numbers across all messages from its devices, enabling applications to detect lost messages. Many edge nodes implement local data processing, filtering, and aggregation before publishing to reduce bandwidth and broker load.",[399,9944,9946],{"id":9945},"primary-application-pattern","Primary Application Pattern",[15,9948,9949],{},"The primary application concept allows edge nodes to adapt their behavior based on application state. A designated primary application publishes STATE messages indicating its operational status. Edge nodes subscribe to these STATE messages and adjust their reporting frequency or metrics based on whether the primary application is online.",[15,9951,9952],{},"For example, an edge node might publish data every second when the primary SCADA application is connected but reduce to every 60 seconds when no primary application is available. This adaptive behavior conserves bandwidth and broker resources while ensuring data availability when needed.",[399,9954,9956],{"id":9955},"command-and-control-flow","Command and Control Flow",[15,9958,9959,9960,1413,9963,9966,9967,2642,9970,9973],{},"Sparkplug B enables bidirectional communication through ",[924,9961,9962],{},"NCMD",[924,9964,9965],{},"DCMD"," messages. Applications publish commands to specific topics, and edge nodes or devices execute the commands and respond with updated metric values in ",[924,9968,9969],{},"NDATA",[924,9971,9972],{},"DDATA"," messages.",[15,9975,9976],{},"Command messages use the same Protocol Buffer format as data messages but flow in the opposite direction. Applications might send a write command to change a setpoint, a rebirth command to request fresh birth certificates, or a custom command to trigger device-specific actions. The standardized command structure enables generic control applications that work with any Sparkplug-compliant device.",[31,9978,9980],{"id":9979},"the-mqtt-sparkplug-specification-for-iiot","The MQTT Sparkplug Specification for IIoT",[15,9982,9983],{},"Now that we have an overview of Sparkplug B and its role in standardizing data formats, it’s time to dive deeper into how this protocol structures its payloads and topics. Understanding these details will give you insight into how Sparkplug B efficiently manages data in complex industrial environments and will assist you in implementing it effectively in your own projects.",[15,9985,9986],{},"Sparkplug B utilizes Google Protocol Buffers (Protobufs) for encoding its messages. Protobufs offer a compact and fast way to serialize structured data, preserving MQTT's lightweight nature while introducing a robust framework for handling complex data.",[399,9988,9980],{"id":9989},"the-mqtt-sparkplug-specification-for-iiot-1",[15,9991,9992],{},"The Sparkplug B specification addresses critical industrial requirements that standard MQTT implementations typically handle inconsistently. The specification is built around several core concepts that work together to create a robust industrial messaging framework.",[399,9994,9996],{"id":9995},"topic-namespace-architecture","Topic Namespace Architecture",[15,9998,9999,10000,10003,10004,10007],{},"Sparkplug B enforces a hierarchical topic namespace that follows a specific pattern: ",[924,10001,10002],{},"spBv1.0\u002F{group_id}\u002F{message_type}\u002F{edge_node_id}\u002F{device_id}",". The namespace starts with the protocol version identifier ",[924,10005,10006],{},"spBv1.0",", ensuring clients can identify Sparkplug messages. The group_id provides logical grouping such as factory, building, or region. The message_type specifies whether this is a birth certificate, data update, death notification, or command. The edge_node_id identifies the gateway or edge node, and optionally, a device_id can specify individual devices under that edge node.",[15,10009,10010,10011,10014],{},"For example, a temperature sensor in a manufacturing facility might publish to ",[924,10012,10013],{},"spBv1.0\u002FManufacturing\u002FDDATA\u002FGateway01\u002FTempSensor05",". This structured approach eliminates ambiguity and enables automatic topic subscription patterns.",[399,10016,10018],{"id":10017},"protocol-buffer-payloads","Protocol Buffer Payloads",[15,10020,10021,10022,10026],{},"Rather than using JSON or other text-based formats, Sparkplug B employs ",[63,10023,10025],{"href":10024},"\u002Fblog\u002F2025\u002F12\u002Fnode-red-buffer-parser-industrial-data\u002F","Google Protocol Buffers"," for message encoding. This choice delivers several advantages in industrial environments. The compact binary format produces significantly smaller messages than JSON, reducing bandwidth consumption on constrained networks. Protocol Buffers provide strongly typed data fields, eliminating parsing ambiguities. The format supports backward compatibility, allowing older clients to work with newer message versions. Finally, efficient parsing performance matters when handling thousands of messages per second.",[399,10028,10030],{"id":10029},"state-management-system","State Management System",[15,10032,10033,10034,10036,10037,10040],{},"The specification includes explicit state awareness through birth and death certificates. When an edge node or device connects to the broker, it publishes a birth certificate (",[924,10035,9941],{}," for nodes, ",[924,10038,10039],{},"DBIRTH"," for devices) that declares all available metrics, their data types, and initial values. This self-description capability means applications can discover device capabilities automatically without external configuration files.",[15,10042,10043,10044,810,10047,10050],{},"When devices disconnect, either gracefully or due to network failure, death certificates (",[924,10045,10046],{},"NDEATH",[924,10048,10049],{},"DDEATH",") signal the disconnection to all subscribers. The MQTT Last Will and Testament feature ensures death certificates publish even when devices lose connectivity unexpectedly. Sequence numbers in every message enable detection of lost messages, while timestamps provide temporal context for all data points.",[399,10052,10054],{"id":10053},"metric-definition-framework","Metric Definition Framework",[15,10056,10057],{},"Each metric in Sparkplug B carries comprehensive metadata beyond just a name and value. The specification supports a wide range of data types including various integer sizes (Int8 through Int64, both signed and unsigned), floating-point numbers (Float and Double), Boolean values, strings, timestamps, UUIDs, binary data, and complex structures like datasets and templates.",[15,10059,10060],{},"Every metric includes a timestamp indicating when the value was captured, not just when it was transmitted. Quality flags indicate whether data is historical (stored and forwarded), transient (not to be stored), or null (sensor failure). This rich metadata enables applications to make informed decisions about data processing and storage.",[399,10062,10064],{"id":10063},"message-types-and-their-purposes","Message Types and Their Purposes",[15,10066,10067,10068,10070,10071,10073,10074,10076,10077,853,10079,4972,10081,10083,10084,1413,10086,10088],{},"Sparkplug B defines specific message types for different communication needs. ",[924,10069,9941],{}," messages announce edge node connection and available metrics. ",[924,10072,9969],{}," messages carry periodic metric updates from edge nodes. ",[924,10075,10046],{}," messages signal edge node disconnection. ",[924,10078,10039],{},[924,10080,9972],{},[924,10082,10049],{}," provide the same functions for devices under edge nodes.",[924,10085,9962],{},[924,10087,9965],{}," enable command and control, allowing applications to send instructions to nodes and devices. STATE messages, published by primary applications, indicate application health and readiness.",[31,10090,10092],{"id":10091},"sparkplug-b-vs-plain-mqtt-understanding-the-differences","Sparkplug B vs Plain MQTT: Understanding the Differences",[15,10094,10095,10096,10099,10100,10103,10104,10107],{},"While both ",[63,10097,10098],{"href":1399},"plain MQTT"," and Sparkplug B use the same MQTT transport protocol, they solve ",[19,10101,10102],{},"very different problems",".\nPlain MQTT focuses on moving messages reliably, whereas Sparkplug B defines ",[19,10105,10106],{},"how industrial data should be structured, identified, and managed"," across devices and applications.",[15,10109,10110],{},"The table below highlights the practical differences that matter when designing real-world IIoT and SCADA systems.",[6376,10112,10113,10126],{},[6379,10114,10115],{},[6382,10116,10117,10120,10123],{},[6385,10118,10119],{},"Category",[6385,10121,10122],{},"Plain MQTT",[6385,10124,10125],{},"MQTT Sparkplug B",[6406,10127,10128,10141,10154,10170,10183,10196,10209,10222,10235,10248,10261,10274,10287,10300,10313,10326,10339,10352,10365,10378,10391],{},[6382,10129,10130,10135,10138],{},[6411,10131,10132],{},[19,10133,10134],{},"What it is",[6411,10136,10137],{},"Messaging protocol",[6411,10139,10140],{},"Industrial IoT specification built on MQTT",[6382,10142,10143,10148,10151],{},[6411,10144,10145],{},[19,10146,10147],{},"Primary purpose",[6411,10149,10150],{},"Message transport",[6411,10152,10153],{},"Standardized industrial data exchange",[6382,10155,10156,10161,10164],{},[6411,10157,10158],{},[19,10159,10160],{},"Topic structure",[6411,10162,10163],{},"Fully custom, no enforced rules",[6411,10165,10166,10167,8879],{},"Strict, standardized namespace (",[924,10168,10169],{},"spBv1.0\u002F...",[6382,10171,10172,10177,10180],{},[6411,10173,10174],{},[19,10175,10176],{},"Payload format",[6411,10178,10179],{},"JSON, text, or custom binary",[6411,10181,10182],{},"Google Protocol Buffers (binary, compact)",[6382,10184,10185,10190,10193],{},[6411,10186,10187],{},[19,10188,10189],{},"Payload consistency",[6411,10191,10192],{},"Varies by device and vendor",[6411,10194,10195],{},"Guaranteed consistent structure",[6382,10197,10198,10203,10206],{},[6411,10199,10200],{},[19,10201,10202],{},"Data typing",[6411,10204,10205],{},"Not enforced",[6411,10207,10208],{},"Strongly typed metrics",[6382,10210,10211,10216,10219],{},[6411,10212,10213],{},[19,10214,10215],{},"Device discovery",[6411,10217,10218],{},"Manual configuration",[6411,10220,10221],{},"Automatic via NBIRTH \u002F DBIRTH",[6382,10223,10224,10229,10232],{},[6411,10225,10226],{},[19,10227,10228],{},"State awareness",[6411,10230,10231],{},"Limited (LWT only)",[6411,10233,10234],{},"Full lifecycle (BIRTH, DATA, DEATH)",[6382,10236,10237,10242,10245],{},[6411,10238,10239],{},[19,10240,10241],{},"Message loss detection",[6411,10243,10244],{},"Not supported",[6411,10246,10247],{},"Sequence numbers included",[6382,10249,10250,10255,10258],{},[6411,10251,10252],{},[19,10253,10254],{},"Timestamps",[6411,10256,10257],{},"Optional, application-defined",[6411,10259,10260],{},"Mandatory per metric",[6382,10262,10263,10268,10271],{},[6411,10264,10265],{},[19,10266,10267],{},"Data quality flags",[6411,10269,10270],{},"Custom implementation",[6411,10272,10273],{},"Built-in (historical, transient, null)",[6382,10275,10276,10281,10284],{},[6411,10277,10278],{},[19,10279,10280],{},"Bandwidth efficiency",[6411,10282,10283],{},"Lower (JSON overhead)",[6411,10285,10286],{},"Higher (protobuf + metric aliasing)",[6382,10288,10289,10294,10297],{},[6411,10290,10291],{},[19,10292,10293],{},"Metric aliasing",[6411,10295,10296],{},"Not available",[6411,10298,10299],{},"Supported",[6382,10301,10302,10307,10310],{},[6411,10303,10304],{},[19,10305,10306],{},"Command & control",[6411,10308,10309],{},"Custom topics and logic",[6411,10311,10312],{},"Standardized NCMD \u002F DCMD",[6382,10314,10315,10320,10323],{},[6411,10316,10317],{},[19,10318,10319],{},"Interoperability",[6411,10321,10322],{},"Low (vendor-specific)",[6411,10324,10325],{},"High (vendor-neutral)",[6382,10327,10328,10333,10336],{},[6411,10329,10330],{},[19,10331,10332],{},"Human readability",[6411,10334,10335],{},"High",[6411,10337,10338],{},"Low (binary encoded)",[6382,10340,10341,10346,10349],{},[6411,10342,10343],{},[19,10344,10345],{},"Debugging effort",[6411,10347,10348],{},"Easy with basic tools",[6411,10350,10351],{},"Requires Sparkplug-aware tools",[6382,10353,10354,10359,10362],{},[6411,10355,10356],{},[19,10357,10358],{},"Scalability",[6411,10360,10361],{},"Depends on custom design",[6411,10363,10364],{},"Designed for large-scale IIoT",[6382,10366,10367,10372,10375],{},[6411,10368,10369],{},[19,10370,10371],{},"Security",[6411,10373,10374],{},"MQTT-level (TLS, ACLs)",[6411,10376,10377],{},"MQTT-level (same as plain MQTT)",[6382,10379,10380,10385,10388],{},[6411,10381,10382],{},[19,10383,10384],{},"Learning curve",[6411,10386,10387],{},"Low",[6411,10389,10390],{},"Medium to high",[6382,10392,10393,10398,10401],{},[6411,10394,10395],{},[19,10396,10397],{},"Best fit use cases",[6411,10399,10400],{},"Small systems, prototypes, simple sensors",[6411,10402,10403],{},"SCADA, HMI, multi-vendor industrial systems",[31,10405,10407],{"id":10406},"using-mqtt-sparkplug-b-with-node-red","Using MQTT Sparkplug B with Node-RED",[15,10409,10410],{},"Now that we understand what Sparkplug B is and how it structures industrial data, let’s see how to use it in practice with Node-RED.",[15,10412,10413,10416],{},[63,10414,811],{"href":10415},"\u002Fnode-red\u002F"," is a popular low-code tool for building IoT and industrial data flows. It’s widely used at the edge to connect devices, process data, and integrate with MQTT brokers. With dedicated Sparkplug nodes, Node-RED makes it easy to publish and consume Sparkplug B messages without manually handling topics, protobufs, or state management.",[15,10418,10419,10420,10422],{},"To run Node-RED reliably in production, especially for industrial and edge deployments, ",[63,10421,803],{"href":810}," provides a managed platform for deploying, scaling, and managing Node-RED instances. FlowFuse also includes a built-in MQTT broker, making it simple to get started with Sparkplug B without additional infrastructure.",[15,10424,10425],{},"[Create a free FlowFuse account]({% include \"sign-up-url.njk\" %}) to deploy Node-RED, connect devices, and start working with MQTT Sparkplug B in minutes.",[15,10427,10428],{},"In the following example, we’ll configure a Node-RED flow where a factory machine publishes temperature and humidity data using Sparkplug B, and then consumes that data downstream.",[399,10430,6240],{"id":6239},[15,10432,4653],{},[39,10434,10435,10444],{},[42,10436,10437,10438,10443],{},"node-red-contrib-mqtt-sparkplug-plus: Install this ",[63,10439,10442],{"href":10440,"rel":10441},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-mqtt-sparkplug-plus",[67],"Node-RED package for Sparkplug B"," support via palette manager.",[42,10445,10446,10447,29],{},"MQTT Broker: An MQTT broker is required to send and receive data between clients. If you do not already have one, FlowFuse offers a built-in MQTT broker service that simplifies the process of using MQTT with Node-RED, no external setup required. To learn how to use the FlowFuse MQTT Broker and create and manage clients, refer to the ",[63,10448,10450],{"href":10449},"\u002Fdocs\u002Fuser\u002Fteambroker\u002F","FlowFuse MQTT documentation",[399,10452,10454],{"id":10453},"configuring-node-red-for-mqtt-sparkplug-b","Configuring Node-RED for MQTT Sparkplug B",[271,10456,10457,10460,10463,10477,10480,10483],{},[42,10458,10459],{},"Drag any mqtt sparkplug node onto the canvas.",[42,10461,10462],{},"Double-click the mqtt sparkplug node to open the configuration panel.",[42,10464,10465,10466,10468,10469,10472,10473,10476],{},"Click the \"+\" icon next to the \"Broker\" field. Enter your MQTT broker's host address (e.g., ",[924,10467,7652],{},"), specify the port number (e.g., ",[924,10470,10471],{},"1883"," for unencrypted or ",[924,10474,10475],{},"8883"," for TLS), and configure the TLS settings if required. Enter the username and password, enter a Client ID, Set the \"Keep Alive\" interval (default is 60 seconds).",[42,10478,10479],{},"Switch to the Sparkplug tab by clicking the Sparkplug option in the top-right corner.",[42,10481,10482],{},"Enter a name in the \"Name\" field (this will be the Edge Node ID). Enter the group name in the \"Group\" field. Select \"No\" for the compression setting. Enable the \"Use Alias for Metrics\" option if you prefer not to send the full metric names every time and use aliases instead.",[42,10484,10485],{},"Click \"Add\" to save the configuration.",[15,10487,10488,10492,10494],{},[165,10489],{"alt":10490,"src":10491,"title":10490},"Screenshot showing the configuration of Sparkplug broker config node","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fmqtt-broker-config.png",[5368,10493],{},[170,10495,10490],{},[399,10497,10499],{"id":10498},"sending-data-to-mqtt-with-sparkplug-b","Sending Data to MQTT with Sparkplug B",[271,10501,10502,10630,10633],{},[42,10503,10504,10505,10507,10508],{},"Drag the inject node onto the canvas. Set the ",[924,10506,1227],{}," to the metrics you want to send and set the repeat interval according to your preference. This inject node could be any node that triggers the data sending. For testing purpose, you can use the following JSONata expression to simulate temperature and humidly metrics:",[1756,10509,10511],{"className":5097,"code":10510,"language":5099,"meta":103,"style":103},"{\n    \"metrics\": [\n        {\n            \"name\": \"sensor\u002Ftemperature\",\n            \"value\": $random() * 100\n        },\n        {\n            \"name\": \"sensor\u002Fhumidity\",\n            \"value\": $random() * 100\n        }\n    ]\n}\n",[924,10512,10513,10517,10531,10536,10557,10574,10579,10583,10602,10616,10621,10626],{"__ignoreMap":103},[1107,10514,10515],{"class":1765,"line":1766},[1107,10516,9062],{"class":1783},[1107,10518,10519,10522,10525,10527,10529],{"class":1765,"line":107},[1107,10520,10521],{"class":1783},"    \"",[1107,10523,10524],{"class":1775},"metrics",[1107,10526,1970],{"class":1783},[1107,10528,992],{"class":1783},[1107,10530,1937],{"class":1783},[1107,10532,10533],{"class":1765,"line":294},[1107,10534,10535],{"class":1783},"        {\n",[1107,10537,10538,10541,10544,10546,10548,10550,10553,10555],{"class":1765,"line":104},[1107,10539,10540],{"class":1783},"            \"",[1107,10542,1914],{"class":10543},"sBMFI",[1107,10545,1970],{"class":1783},[1107,10547,992],{"class":1783},[1107,10549,1963],{"class":1783},[1107,10551,10552],{"class":1966},"sensor\u002Ftemperature",[1107,10554,1970],{"class":1783},[1107,10556,9079],{"class":1783},[1107,10558,10559,10561,10564,10566,10568,10571],{"class":1765,"line":1868},[1107,10560,10540],{"class":1783},[1107,10562,10563],{"class":10543},"value",[1107,10565,1970],{"class":1783},[1107,10567,992],{"class":1783},[1107,10569,10570],{"class":1779}," $random() * ",[1107,10572,10573],{"class":1854},"100\n",[1107,10575,10576],{"class":1765,"line":1896},[1107,10577,10578],{"class":1783},"        },\n",[1107,10580,10581],{"class":1765,"line":1623},[1107,10582,10535],{"class":1783},[1107,10584,10585,10587,10589,10591,10593,10595,10598,10600],{"class":1765,"line":1924},[1107,10586,10540],{"class":1783},[1107,10588,1914],{"class":10543},[1107,10590,1970],{"class":1783},[1107,10592,992],{"class":1783},[1107,10594,1963],{"class":1783},[1107,10596,10597],{"class":1966},"sensor\u002Fhumidity",[1107,10599,1970],{"class":1783},[1107,10601,9079],{"class":1783},[1107,10603,10604,10606,10608,10610,10612,10614],{"class":1765,"line":1930},[1107,10605,10540],{"class":1783},[1107,10607,10563],{"class":10543},[1107,10609,1970],{"class":1783},[1107,10611,992],{"class":1783},[1107,10613,10570],{"class":1779},[1107,10615,10573],{"class":1854},[1107,10617,10618],{"class":1765,"line":1940},[1107,10619,10620],{"class":1783},"        }\n",[1107,10622,10623],{"class":1765,"line":1976},[1107,10624,10625],{"class":1783},"    ]\n",[1107,10627,10628],{"class":1765,"line":2002},[1107,10629,8016],{"class":1783},[42,10631,10632],{},"Drag the mqtt sparkplug device node onto the canvas.",[42,10634,10635],{},"Double-click the mqtt sparkplug device node to open the configuration panel. Add the metric names that you will be sending by clicking the bottom-left \"Add\" button. Ensure that the names match the metric names in the payload you are sending and specify the data types for each metric.",[15,10637,10638,10643,10645],{},[165,10639],{"alt":10640,"src":10641,"title":10642},"Screenshot showing the Sparkplug Device node configuration and the \"Add\" button for defining metrics","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fmqtt-sparkplug-device-node.png","Screenshot showing the Sparkplug Device node configuration and the 'Add' button for defining metrics",[5368,10644],{},[170,10646,10640],{},[271,10648,10649,10652],{"start":104},[42,10650,10651],{},"Switch to the Advanced tab by clicking the \"Advanced\" option at the top-right.",[42,10653,10654,10655,10657,10658,10660],{},"Enable the \"Send Birth Immediately\" option. This ensures that a Birth message (DBIRTH) is sent immediately upon deployment and connection to the MQTT broker. Note that enabling this option will send the ",[924,10656,10039],{}," message when the device node connects, but an ",[924,10659,9941],{}," message will be sent successful connection of mqtt sparkplug out node if you are using.",[15,10662,10663,10666,10668],{},[165,10664],{"alt":10640,"src":10665,"title":10642},"\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fmqtt-spark-device-advance.png",[5368,10667],{},[170,10669,10640],{},[271,10671,10672,10675,10678],{"start":1896},[42,10673,10674],{},"Optionally, enable Store and Forward when not connected to ensure that messages are stored and sent once the connection is re-established. To use this option, make sure you have enabled it in the mqtt sparkplug broker config node and specified the destination.",[42,10676,10677],{},"Connect the inject node's output to the mqtt sparkplug device node's input.",[42,10679,10680],{},"Deploy the flow by clicking the top-right \"Deploy\" button.",[15,10682,10683,10684,10686],{},"Once you deploy the flow and all devices connect to the MQTT broker, the system automatically send a ",[924,10685,10039],{}," message as soon as each device within the node connects, signalling that the device is ready for data transmission.",[15,10688,4917,10689,1150],{},[1107,10690,10691,10692,10694,10695,10698,10699,10701],{},"{\"id\":\"f2864f2b830e3590\",\"type\":\"mqtt sparkplug device\",\"z\":\"239c9025714089d3\",\"name\":\"Machine1\",\"metrics\":{\"sensor\u002Ftemperature\":{\"dataType\":\"Float\",\"name\":\"sensor\u002Ftemperature\"},\"sensor\u002Fhumidity\":{\"dataType\":\"Float\",\"name\":\"sensor\u002Fhumidity\"}},\"broker\":\"0d831bd9ba588536\",\"birthImmediately\":true,\"bufferDevice\":false,\"x\":380,\"y\":320,\"wires\":[[\"3dfc9b74f5e36bec\"]]},{\"id\":\"90cc413f58871fc1\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send Metrics\",\"props\":",[1107,10693,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"{    \"metrics\": ",[1107,10696,10697],{},"        {            \"name\": \"sensor\u002Ftemperature\",            \"value\": $random()*100        },        {            \"name\": \"sensor\u002Fhumidity\",            \"value\": $random()*100        }","}\",\"payloadType\":\"jsonata\",\"x\":130,\"y\":320,\"wires\":[[\"f2864f2b830e3590\"]]},{\"id\":\"3dfc9b74f5e36bec\",\"type\":\"debug\",\"z\":\"239c9025714089d3\",\"name\":\"\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":650,\"y\":320,\"wires\":",[1107,10700],{},"},{\"id\":\"0d831bd9ba588536\",\"type\":\"mqtt-sparkplug-broker\",\"name\":\"Local Host\",\"deviceGroup\":\"My Devices\",\"eonName\":\"Node-Red\",\"broker\":\"localhost\",\"port\":\"1883\",\"tls\":\"\",\"clientid\":\"\",\"usetls\":false,\"protocolVersion\":\"4\",\"keepalive\":\"60\",\"cleansession\":true,\"enableStoreForward\":false,\"compressAlgorithm\":\"\",\"aliasMetrics\":true,\"manualEoNBirth\":false,\"primaryScada\":\"\"}",[399,10703,10705],{"id":10704},"receiving-data-from-mqtt-with-sparkplug-b","Receiving Data from MQTT with Sparkplug B",[271,10707,10708,10711,10714,10728,10731,10734,10737],{},[42,10709,10710],{},"Drag the mqtt sparkplug in node onto the canvas.",[42,10712,10713],{},"Double-click the node and configure the broker settings.",[42,10715,10716,10717,10720,10721,10723,10724,10727],{},"Enter the topic in the \"Topic\" field in the format ",[924,10718,10719],{},"namespace\u002Fgroup_id\u002Fmessage_type\u002Fedge_node_id\u002F[device_id]",". Use ",[924,10722,9972],{}," for receiving metrics you are sending using device node or a wildcard like ",[924,10725,10726],{},"spBv1.0\u002Fgroup_id\u002F+\u002F+\u002F[device_id]"," to listen to all message types from a specific device.",[42,10729,10730],{},"Select the desired \"QoS\" level.",[42,10732,10733],{},"Drag a debug node onto the canvas.",[42,10735,10736],{},"Connect the mqtt sparkplug in node’s output to the debug node’s input.",[42,10738,10739],{},"Click \"Deploy\" to save and run the flow.",[15,10741,10742,10743,4972,10745,10747],{},"Now you will be able to see the ",[924,10744,10039],{},[924,10746,9972],{}," messages printed on the debug panel.",[15,10749,4917,10750,1150],{},[1107,10751,10752,10753,10701],{},"{\"id\":\"a98c49d80bb5c4ee\",\"type\":\"mqtt sparkplug in\",\"z\":\"239c9025714089d3\",\"name\":\"\",\"topic\":\"spBv1.0\u002FMy Devices\u002FDDATA\u002FNode-RED\u002FMachine1\",\"qos\":\"2\",\"broker\":\"0d831bd9ba588536\",\"x\":330,\"y\":120,\"wires\":[[\"655761fb21409216\"]]},{\"id\":\"655761fb21409216\",\"type\":\"debug\",\"z\":\"239c9025714089d3\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":800,\"y\":120,\"wires\":",[1107,10754],{},[399,10756,10758],{"id":10757},"sending-commands-for-devices-and-eon-nodes","Sending Commands for devices and EoN nodes",[15,10760,10761],{},"Beyond data exchange, MQTT Sparkplug B allows you to send commands for managing devices and Edge of Network (EoN) nodes, such as initiating a device's rebirth or signalling its death.",[271,10763,10764,10767],{},[42,10765,10766],{},"Drag inject node onto the canvas.",[42,10768,2105,10769,10771],{},[924,10770,945],{}," in the inject node to the desired command. For instance, you can use the following JSON object to send a command that triggers a device's death:",[1756,10773,10775],{"className":5097,"code":10774,"language":5099,"meta":103,"style":103},"    {\n        \"device\" : {\n            \"death\" : true\n        }\n    }   \n",[924,10776,10777,10782,10798,10812,10816],{"__ignoreMap":103},[1107,10778,10779],{"class":1765,"line":1766},[1107,10780,10781],{"class":1783},"    {\n",[1107,10783,10784,10787,10790,10792,10795],{"class":1765,"line":107},[1107,10785,10786],{"class":1783},"        \"",[1107,10788,10789],{"class":1775},"device",[1107,10791,1970],{"class":1783},[1107,10793,10794],{"class":1783}," :",[1107,10796,10797],{"class":1783}," {\n",[1107,10799,10800,10802,10805,10807,10809],{"class":1765,"line":294},[1107,10801,10540],{"class":1783},[1107,10803,10804],{"class":10543},"death",[1107,10806,1970],{"class":1783},[1107,10808,10794],{"class":1783},[1107,10810,10811],{"class":1783}," true\n",[1107,10813,10814],{"class":1765,"line":104},[1107,10815,10620],{"class":1783},[1107,10817,10818],{"class":1765,"line":1868},[1107,10819,10820],{"class":1783},"    }\n",[15,10822,10823],{},"Alternatively, to send a command that triggers a device's rebirth, use:",[1756,10825,10827],{"className":5097,"code":10826,"language":5099,"meta":103,"style":103},"    {\n        \"device\" : {\n            \"rebirth\" : true\n        }\n    }   \n",[924,10828,10829,10833,10845,10858,10862],{"__ignoreMap":103},[1107,10830,10831],{"class":1765,"line":1766},[1107,10832,10781],{"class":1783},[1107,10834,10835,10837,10839,10841,10843],{"class":1765,"line":107},[1107,10836,10786],{"class":1783},[1107,10838,10789],{"class":1775},[1107,10840,1970],{"class":1783},[1107,10842,10794],{"class":1783},[1107,10844,10797],{"class":1783},[1107,10846,10847,10849,10852,10854,10856],{"class":1765,"line":294},[1107,10848,10540],{"class":1783},[1107,10850,10851],{"class":10543},"rebirth",[1107,10853,1970],{"class":1783},[1107,10855,10794],{"class":1783},[1107,10857,10811],{"class":1783},[1107,10859,10860],{"class":1765,"line":104},[1107,10861,10620],{"class":1783},[1107,10863,10864],{"class":1765,"line":1868},[1107,10865,10820],{"class":1783},[271,10867,10868,10871,10874],{"start":294},[42,10869,10870],{},"Connect the output of the inject node to the input of the relevant mqtt sparkplug device node.",[42,10872,10873],{},"Deploy the flow by clicking the Deploy button at the top-right of the Node-RED interface.",[42,10875,10876],{},"Click the inject node’s button to send the command.",[15,10878,10879,10880,29],{},"In this example, we've used an inject node to manually send commands, but you can also trigger these commands based on other inputs or conditions within your flow, such as device status or sensor data. For more information on available commands and advanced configurations, refer to the ",[63,10881,10883],{"href":10440,"rel":10882},[67],"MQTT Sparkplug nodes documentation",[15,10885,4917,10886,1150],{},[1107,10887,10888,10889,10892,10893,10895,10896,10899,10900,10903],{},"{\"id\":\"f2864f2b830e3590\",\"type\":\"mqtt sparkplug device\",\"z\":\"239c9025714089d3\",\"name\":\"Machine1\",\"metrics\":{\"sensor\u002Ftemperature\":{\"dataType\":\"Float\"},\"sensor\u002Fhumidity\":{\"dataType\":\"Float\"}},\"broker\":\"0d831bd9ba588536\",\"birthImmediately\":true,\"bufferDevice\":false,\"x\":440,\"y\":320,\"wires\":[[\"3dfc9b74f5e36bec\"]]},{\"id\":\"90cc413f58871fc1\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send connect command\",\"props\":",[1107,10890,10891],{},"{\"p\":\"command\",\"v\":\"{\"node\":{\"connect\":true}}\",\"vt\":\"jsonata\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":170,\"y\":260,\"wires\":[[\"f2864f2b830e3590\"]]},{\"id\":\"3dfc9b74f5e36bec\",\"type\":\"debug\",\"z\":\"239c9025714089d3\",\"name\":\"\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":650,\"y\":320,\"wires\":",[1107,10894],{},"},{\"id\":\"915ca0772eebee04\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send rebirth command\",\"props\":",[1107,10897,10898],{},"{\"p\":\"command\",\"v\":\"{\"device\":{\"rebirth\":true}}\",\"vt\":\"json\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":160,\"y\":320,\"wires\":[[\"f2864f2b830e3590\"]]},{\"id\":\"696db58cc9eb029d\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send death command\",\"props\":",[1107,10901,10902],{},"{\"p\":\"command\",\"v\":\"{\"device\":{\"death\":true}}\",\"vt\":\"json\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":160,\"y\":380,\"wires\":[[\"f2864f2b830e3590\"]]},{\"id\":\"0d831bd9ba588536\",\"type\":\"mqtt-sparkplug-broker\",\"name\":\"Local Host\",\"deviceGroup\":\"My Devices\",\"eonName\":\"Node-Red\",\"broker\":\"localhost\",\"port\":\"1883\",\"tls\":\"\",\"clientid\":\"\",\"usetls\":false,\"protocolVersion\":\"4\",\"keepalive\":\"60\",\"cleansession\":true,\"enableStoreForward\":false,\"compressAlgorithm\":\"\",\"aliasMetrics\":true,\"manualEoNBirth\":true,\"primaryScada\":\"\"}",[15,10905,10906],{},"If you need more flexibility in defining topic names when sending data, you can use the mqtt sparkplug out node. It’s quite similar to the standard mqtt out node but is designed to handle Sparkplug-encoded messages. Below is an example showing how to use the mqtt sparkplug out node with in nodes.",[15,10908,4917,10909,1150],{},[1107,10910,10911,10912,10914,10915,10917,10918,10694,10920,10923],{},"{\"id\":\"bbe3765e67eed956\",\"type\":\"mqtt sparkplug in\",\"z\":\"f098830cc10afc2f\",\"name\":\"\",\"topic\":\"spBv1.0\u002F+\u002F+\u002F#\",\"qos\":\"2\",\"broker\":\"0d831bd9ba588536\",\"x\":150,\"y\":100,\"wires\":[[\"d45ff4446380beaa\"]]},{\"id\":\"3b2b9788c51d5c3b\",\"type\":\"mqtt sparkplug out\",\"z\":\"f098830cc10afc2f\",\"name\":\"\",\"topic\":\"spBv1.0\u002FMy Devices\u002FNDATA\u002FNode-Red\",\"qos\":\"\",\"retain\":\"\",\"broker\":\"0d831bd9ba588536\",\"x\":510,\"y\":200,\"wires\":",[1107,10913],{},"},{\"id\":\"d45ff4446380beaa\",\"type\":\"debug\",\"z\":\"f098830cc10afc2f\",\"name\":\"\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":410,\"y\":100,\"wires\":",[1107,10916],{},"},{\"id\":\"dc73048fd385783a\",\"type\":\"inject\",\"z\":\"f098830cc10afc2f\",\"name\":\"Send Metrics\",\"props\":",[1107,10919,5884],{},[1107,10921,10922],{},"        {            \"name\": \"sensor\u002Ftemperature\",            \"value\": $random(),            \"type\": \"Float\"        },        {            \"name\": \"sensor\u002Fhumidity\",            \"value\": $random(),            \"type\": \"Float\"        }","}\",\"payloadType\":\"jsonata\",\"x\":170,\"y\":220,\"wires\":[[\"3b2b9788c51d5c3b\"]]},{\"id\":\"0d831bd9ba588536\",\"type\":\"mqtt-sparkplug-broker\",\"name\":\"Local Host\",\"deviceGroup\":\"My Devices\",\"eonName\":\"Node-Red\",\"broker\":\"localhost\",\"port\":\"1883\",\"tls\":\"\",\"clientid\":\"\",\"usetls\":false,\"protocolVersion\":\"4\",\"keepalive\":\"60\",\"cleansession\":true,\"enableStoreForward\":false,\"compressAlgorithm\":\"\",\"aliasMetrics\":true,\"manualEoNBirth\":false,\"primaryScada\":\"\"}",[15,10925,10926,10931],{},[165,10927],{"alt":10928,"src":10929,"title":10930},"\"Images of some Sparkplug messages printed on debug panel\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fsparkplug-messages.png","Images of some Sparkplug messages printed on debug panel",[170,10932,10930],{},[31,10934,10936],{"id":10935},"best-practices-for-production-deployments","Best Practices for Production Deployments",[15,10938,10939],{},"Getting Sparkplug B working is only the first step. Running it reliably in production means thinking about naming, state, performance, and failure handling from day one. The best practices below focus on the things that matter most once your system is live, keeping data trustworthy, networks efficient, and recovery predictable.",[39,10941,10942,10948,10959,10965,10975,10981,10987],{},[42,10943,10944,10947],{},[19,10945,10946],{},"Name things like you mean it","\nUse clear group, edge, and device names that match your factory or system layout. Future you will thank you.",[42,10949,10950,10953,10954,1413,10956,10958],{},[19,10951,10952],{},"Birth messages are non-negotiable","\nAlways send ",[924,10955,9941],{},[924,10957,10039],{}," on start and reconnect. If apps don’t see a birth, they don’t trust the data.",[42,10960,10961,10964],{},[19,10962,10963],{},"Alias your metrics","\nTurn on metric aliasing to cut payload size and keep networks fast and efficient.",[42,10966,10967,10970,1413,10972,10974],{},[19,10968,10969],{},"Listen for Death messages",[924,10971,10046],{},[924,10973,10049],{}," are early warning signals. Use them to detect failures instantly.",[42,10976,10977,10980],{},[19,10978,10979],{},"Buffer with intent","\nStore-and-forward is great for flaky networks, just don’t let backlogs pile up unchecked.",[42,10982,10983,10986],{},[19,10984,10985],{},"Lock down MQTT early","\nUse TLS, authentication, and topic permissions from day one. Security is easier before go-live.",[42,10988,10989,10992],{},[19,10990,10991],{},"Break it on purpose","\nRestart brokers and edge nodes during testing. A system that recovers cleanly is production-ready.",[31,10994,1425],{"id":1424},[15,10996,10997],{},"MQTT Sparkplug B transforms MQTT from a simple messaging protocol into a robust, standardized framework for industrial IoT. By enforcing structured topics, typed metrics, state awareness, and efficient payloads, it makes integrating heterogeneous devices predictable, scalable, and reliable.",[15,10999,11000],{},"Following best practices (clear naming, birth\u002Fdeath messages, metric aliasing, buffering, security, and deliberate testing) ensures your system runs smoothly in production and avoids costly downtime.",[15,11002,11003],{},"For teams looking to get Sparkplug B up and running quickly, [FlowFuse]({% include \"sign-up-url.njk\" %}) provides a production-ready, easy-to-use platform. With minimal setup and no advanced skills required, you can deploy, scale, and monitor industrial flows in minutes. Start building your reliable, standardized IIoT solution today.",[2582,11005,11006],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":11008},[11009,11012,11017,11025,11026,11033,11034],{"id":9848,"depth":107,"text":9849,"children":11010},[11011],{"id":9874,"depth":294,"text":9875},{"id":9921,"depth":107,"text":9922,"children":11013},[11014,11015,11016],{"id":9928,"depth":294,"text":9929},{"id":9945,"depth":294,"text":9946},{"id":9955,"depth":294,"text":9956},{"id":9979,"depth":107,"text":9980,"children":11018},[11019,11020,11021,11022,11023,11024],{"id":9989,"depth":294,"text":9980},{"id":9995,"depth":294,"text":9996},{"id":10017,"depth":294,"text":10018},{"id":10029,"depth":294,"text":10030},{"id":10053,"depth":294,"text":10054},{"id":10063,"depth":294,"text":10064},{"id":10091,"depth":107,"text":10092},{"id":10406,"depth":107,"text":10407,"children":11027},[11028,11029,11030,11031,11032],{"id":6239,"depth":294,"text":6240},{"id":10453,"depth":294,"text":10454},{"id":10498,"depth":294,"text":10499},{"id":10704,"depth":294,"text":10705},{"id":10757,"depth":294,"text":10758},{"id":10935,"depth":107,"text":10936},{"id":1424,"depth":107,"text":1425},"2024-08-22","Learn how to implement MQTT Sparkplug B with Node-RED for standardized, reliable Industrial IoT data flows and scalable production deployments.","2025-12-22",{"keywords":11039,"excerpt":11040},"node red, mqtt sparkplug, industrial IoT",{"type":12,"value":11041},[11042],[15,11043,9845],{},"\u002Fblog\u002F2024\u002F08\u002Fusing-mqtt-sparkplugb-with-node-red",{"title":9839,"description":11036},{"loc":11044},"blog\u002F2024\u002F08\u002Fusing-mqtt-sparkplugb-with-node-red","Standardize and manage industrial IoT data with MQTT Sparkplug B",[8650,138,8410],"MQTT Sparkplug B extends plain MQTT with standardized topic namespaces, Protocol Buffer payloads, and lifecycle state messages (birth\u002Fdeath) to solve the integration chaos common in industrial IoT. This guide explains the specification and shows how to use the node-red-contrib-mqtt-sparkplug-plus package to publish and receive Sparkplug B messages in Node-RED.","qcbJeRULdsdm-dPPEZH61Qe3xnGS_7V0Mwr6lc92DvE",{"id":11053,"title":11054,"authors":11055,"body":11056,"cta":11280,"date":11283,"description":11284,"extension":116,"image":11285,"lastUpdated":6203,"meta":11286,"navigation":129,"path":11292,"seo":11293,"sitemap":11294,"stem":11295,"subtitle":11296,"tags":11297,"tldr":11299,"video":3,"__hash__":11300},"blog\u002Fblog\u002F2024\u002F08\u002Fopentelemetry-with-node-red.md","Monitoring and Optimizing Node-RED Flows with Open Telemetry (2026)",[144],{"type":12,"value":11057,"toc":11268},[11058,11061,11064,11068,11071,11075,11078,11081,11085,11088,11091,11110,11113,11115,11118,11142,11146,11155,11181,11184,11188,11191,11208,11211,11221,11224,11238,11241,11250,11253,11257,11260,11263,11265],[15,11059,11060],{},"Have you ever found yourself frustrated by unexpected delays in your Node-RED flows, wondering where the bottlenecks are hiding? Even small latency issues can have a big impact on your system's performance. That's where Open Telemetry comes in. With its powerful distributed tracing capabilities, you can finally take control and get a clear view of how your flows are performing in real time.",[15,11062,11063],{},"Integrating Open Telemetry with Node-RED allows you to monitor latency across your flows. By implementing distributed tracing, you’ll gain the ability to see exactly where delays occur, helping you optimize performance and ensure your IoT applications run efficiently.",[31,11065,11067],{"id":11066},"what-is-distributed-tracing-and-how-does-open-telemetry-help","What is Distributed Tracing and How Does Open Telemetry Help?",[15,11069,11070],{},"Distributed tracing is a method used to track and observe the flow of requests through different services within a distributed system. It provides insights into how requests are handled, where delays occur, and how different components interact. By visualizing the path of a request across your system, distributed tracing helps you identify performance bottlenecks and optimize the overall efficiency of your applications.",[399,11072,11074],{"id":11073},"what-is-opentelemetry","What is OpenTelemetry?",[15,11076,11077],{},"Open Telemetry is an open-source framework designed to help you monitor and understand your software systems. It collects and organizes data on how your applications perform and behave, allowing you to track requests as they move through various services. Open Telemetry provides a standardized way to gather and analyze telemetry data, including traces, metrics, and logs, to give you a comprehensive view of your system’s performance.",[15,11079,11080],{},"In Node-RED The Open Telemetry module helps track messages by creating \"spans\" that record details about each message's journey. Every time a message moves from one node to another, a span is created to capture where it came from, where it’s going, and how long it took. These spans are linked together, showing the entire path of the message through the system. This makes it easier to spot slowdowns, fix problems, and improve how data moves through Node-RED. The module also makes sure this tracking information follows the message as it moves across different nodes and external services.",[31,11082,11084],{"id":11083},"tracing-in-node-red-flows-using-opentelemetry","Tracing in Node-RED Flows using Opentelemetry",[15,11086,11087],{},"In a manufacturing plant, Node-RED manages different machines and sensors. Suppose there's a problem with the production line, such as a delay in processing or a GPIO node experiencing issues reading data. With Open Telemetry integrated, you can trace the data flow through the system to see exactly where the issue is happening. This helps you quickly identify whether the problem is with a specific node that is reading the machine data or a delay in data processing, allowing you to fix the issue faster and keep the production line running smoothly.",[15,11089,11090],{},"For demonstration purposes, we will use a flow that simulates sensor reading and data processing. We will monitor this flow using Open Telemetry to track data across the system, identify bottlenecks, and optimize performance.",[15,11092,4917,11093,1150],{},[1107,11094,11095,11096,11099,11100,11102,11103,11105,11106,11109],{},"{\"id\":\"78e4a1255f9d0ad1\",\"type\":\"group\",\"z\":\"45e56b4089cada94\",\"name\":\"\",\"style\":{\"label\":true},\"nodes\":",[1107,11097,11098],{},"\"636ac7b4d798a5c4\",\"ac87db82c2ab66f5\",\"c8a1749629359031\",\"c0d30281031f07db\"",",\"x\":34,\"y\":139,\"w\":852,\"h\":82},{\"id\":\"636ac7b4d798a5c4\",\"type\":\"inject\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"Temperature sensor\",\"props\":",[1107,11101,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"300\",\"payloadType\":\"jsonata\",\"x\":180,\"y\":180,\"wires\":[[\"c8a1749629359031\"]]},{\"id\":\"ac87db82c2ab66f5\",\"type\":\"debug\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":780,\"y\":180,\"wires\":",[1107,11104],{},"},{\"id\":\"c8a1749629359031\",\"type\":\"change\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"Kelvin to Celsius\",\"rules\":",[1107,11107,11108],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload - 273.15\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":400,\"y\":180,\"wires\":[[\"c0d30281031f07db\"]]},{\"id\":\"c0d30281031f07db\",\"type\":\"delay\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"\",\"pauseType\":\"delay\",\"timeout\":\"2\",\"timeoutUnits\":\"seconds\",\"rate\":\"1\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"1\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":600,\"y\":180,\"wires\":[[\"ac87db82c2ab66f5\"]]}",[15,11111,11112],{},"Deploy the flow above, and you might see a delay in the data shown on the debug panel. For this example, we added a Delay node before the Change node that converts temperature data from Kelvin to Celsius. While this delay is visible here, finding such delays in larger flows with many nodes can be difficult and time-consuming. Open Telemetry makes this easier by giving you detailed traces that show where delays or issues are happening",[399,11114,6240],{"id":6239},[15,11116,11117],{},"Before you start, ensure you have the following:",[39,11119,11120,11128],{},[42,11121,11122,11127],{},[63,11123,11126],{"href":11124,"rel":11125},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-opentelemetry",[67],"node-red-contrib-opentelemetry"," : Install this Node-RED module via the Node-RED Palette Manager.",[42,11129,11130,11131,11136,11137,29],{},"Open Telemetry exporter: Set up an Open Telemetry exporter to send trace data to a backend. For details on available exporters, visit ",[63,11132,11135],{"href":11133,"rel":11134},"https:\u002F\u002Fopentelemetry.io\u002Fdocs\u002Finstrumentation\u002Fjs\u002Fexporters\u002F",[67],"Open Telemetry Exporters",". For this guide, I have set up the ",[63,11138,11141],{"href":11139,"rel":11140},"https:\u002F\u002Fjaegertracing.io\u002F",[67],"Jaeger",[399,11143,11145],{"id":11144},"setting-open-telemetry-in-node-red","Setting Open Telemetry in Node-RED",[15,11147,11148,11153],{},[165,11149],{"alt":11150,"src":11151,"title":11152},"\"Screenshot showing the configuration of opentelmetry node\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopentelmetry-node.png","Screenshot showing the configuration of opentelmetry node",[170,11154,11152],{},[271,11156,11157,11162,11169,11172,11175,11178],{},[42,11158,1045,11159,1216],{},[924,11160,11161],{},"OTEL",[42,11163,11164,11165,11168],{},"Double-click on the node and set the URL to your exporter endpoint (e.g., ",[924,11166,11167],{},"http:\u002F\u002Flocalhost:4318\u002Fv1\u002Ftraces"," for a locally running Jaeger exporter). Provide a name for the service according to your preference, and set the Prefix, which will be added to the root Node-RED span name before the initial node name (you can keep it as \"Message\" if preferred).",[42,11170,11171],{},"In the Ignore field, add the names of nodes you want to exclude from Open Telemetry tracing.",[42,11173,11174],{},"In the Propagate field, add the names of nodes if you want them to forward trace headers to external systems or other nodes in the flow. This ensures that these nodes participate in the distributed trace, allowing the trace context to be maintained across different components.",[42,11176,11177],{},"Set the Timeout to define how long (in seconds) the OTEL node should wait before ending and discarding a message that has not been modified.",[42,11179,11180],{},"Now deploy the flow by clicking on the top-right deploy button.",[15,11182,11183],{},"Once the flow is deployed, Open Telemetry will start collecting and sending trace data to your specified exporter.",[399,11185,11187],{"id":11186},"monitoring-performance-using-the-exporter-web-ui","Monitoring Performance Using the Exporter Web UI",[15,11189,11190],{},"Now, let's monitor the performance and latency between each node to identify delays. For this section, I am assuming you have Jaeger running as your exporter.",[271,11192,11193,11199,11202,11205],{},[42,11194,11195,11196,29],{},"Open the Jaeger web UI in your browser. By default, it will be available at ",[924,11197,11198],{},"http:\u002F\u002Flocalhost:16686\u002F",[42,11200,11201],{},"Navigate to the \"Search\" by clicking on the \"Search\" option at the top.",[42,11203,11204],{},"Select the service name that you configured in the OTEL node from the service field. Once selected, you will see all the traces for each interaction in the flow. You can filter the traces by specific nodes using the operation field.",[42,11206,11207],{},"To monitor and find issues, select the desired trace and click on the \"Find Trace\" button. Click on the first trace to examine it.",[15,11209,11210],{},"Once the trace opens, you will see the duration taken by each node to process and pass data. Notice the time taken by the delay node, which is 2 seconds, indicating the problem. By clicking on the green line corresponding to this delay node, you can view more detailed information about the trace.",[15,11212,11213,11218],{},[165,11214],{"alt":11215,"src":11216,"title":11217},"\"Image showing the total duration taken by the flow\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fbefore.png","Image showing the total duration taken by the flow",[170,11219,11220],{},"Image showing the tototal duration taken by the flow",[15,11222,11223],{},"Since the issue was identified with the delay node, let's remove that delay node.",[15,11225,4917,11226,1150],{},[1107,11227,11228,11229,11231,11232,11234,11235,11237],{},"{\"id\":\"636ac7b4d798a5c4\",\"type\":\"inject\",\"z\":\"350fb9fbb98012be\",\"name\":\"Temperature sensor\",\"props\":",[1107,11230,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"300\",\"payloadType\":\"jsonata\",\"x\":200,\"y\":100,\"wires\":[[\"c8a1749629359031\"]]},{\"id\":\"ac87db82c2ab66f5\",\"type\":\"debug\",\"z\":\"350fb9fbb98012be\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":640,\"y\":100,\"wires\":",[1107,11233],{},"},{\"id\":\"c8a1749629359031\",\"type\":\"change\",\"z\":\"350fb9fbb98012be\",\"name\":\"Kelvin to Celsius\",\"rules\":",[1107,11236,11108],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":420,\"y\":100,\"wires\":[[\"ac87db82c2ab66f5\"]]}",[15,11239,11240],{},"After updating the flow, redeploy the flow and check the traces again. You should see that the total time has been reduced significantly, with the overall flow now taking around 8 milliseconds instead of the previous 2 seconds. This demonstrates how Open Telemetry helps in identifying and resolving performance issues in your Node-RED flows.",[15,11242,11243,11248],{},[165,11244],{"alt":11245,"src":11246,"title":11247},"\"Image showing the total duration taken by the flow after fixing the issue\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fafter.png","Image showing the total duration taken by the flow after fixing the issue",[170,11249,11247],{},[15,11251,11252],{},"Throughout this guide, we’ve interacted with an exporter which is running locally. However, by deploying and setting up your exporter on a server, you can remotely monitor the performance of your Node-RED flows. This setup enables you to oversee your system's performance from anywhere, making it easier to detect and address issues promptly.",[31,11254,11256],{"id":11255},"enhancing-monitoring-and-optimization-with-flowfuse","Enhancing Monitoring and Optimization with FlowFuse",[15,11258,11259],{},"While OpenTelemetry excels at tracing and optimizing Node-RED flows, FlowFuse offers a powerful solution for managing and monitoring Node-RED instances. It streamlines the creation, deployment, and management of instances, allowing you to deploy your applications with a single click and minimizing deployment complexity and errors.",[15,11261,11262],{},"FlowFuse also boosts collaboration and security through features like team management, role-based access control, multi-factor authentication, and snapshot recovery. These capabilities ensure effective management, secure access, and easy recovery from changes, making FlowFuse an essential tool for optimizing and overseeing your Node-RED deployments.",[31,11264,1425],{"id":1424},[15,11266,11267],{},"Integrating OpenTelemetry with Node-RED enables you to efficiently trace and resolve delays in your flows, ensuring smoother and more efficient operation of your IoT applications. By following the steps outlined in this guide, you can leverage distributed tracing to identify performance bottlenecks and optimize your flows effectively. With OpenTelemetry's detailed insights and FlowFuse's robust features, you'll be well-equipped to maintain peak performance and manage your Node-RED environment seamlessly.",{"title":103,"searchDepth":104,"depth":104,"links":11269},[11270,11273,11278,11279],{"id":11066,"depth":107,"text":11067,"children":11271},[11272],{"id":11073,"depth":294,"text":11074},{"id":11083,"depth":107,"text":11084,"children":11274},[11275,11276,11277],{"id":6239,"depth":294,"text":6240},{"id":11144,"depth":294,"text":11145},{"id":11186,"depth":294,"text":11187},{"id":11255,"depth":107,"text":11256},{"id":1424,"depth":107,"text":1425},{"type":748,"title":11281,"description":11282},"Manage and Monitor Node-RED at Scale","FlowFuse gives you centralized deployment, team collaboration, role-based access control, and snapshot recovery, everything you need to keep your Node-RED flows running reliably in production.","2024-08-15","Learn to integrate Open Telemetry with Node-RED to track and optimize flow performance.","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopentelemetry-with-node-red.png",{"keywords":11287,"excerpt":11288},"nodered opentelemetry node opentelemetry, opentelemetry nodejs example, opentelemetry node, open telemetry nodejs, node-red distributed tracing, node-red performance monitoring, opentelemetry jaeger node-red",{"type":12,"value":11289},[11290],[15,11291,11060],{},"\u002Fblog\u002F2024\u002F08\u002Fopentelemetry-with-node-red",{"title":11054,"description":11284},{"loc":11292},"blog\u002F2024\u002F08\u002Fopentelemetry-with-node-red","Integrating Open Telemetry with Node-RED for Efficient Distributed Tracing",[3124,138,11298,7457],"node-red tips","This guide explains how to integrate OpenTelemetry with Node-RED using the node-red-contrib-opentelemetry module to perform distributed tracing. By connecting to a Jaeger exporter, you can inspect per-node latency across your flows, pinpoint bottlenecks, and verify that fixes have the intended effect, reducing a 2-second delay to just 8 milliseconds in the worked example.","2MJmZhFXjYGCebpb9NL5v9JHzn3lF990Q1XMOXrFfzE",{"id":11302,"title":11303,"authors":11304,"body":11305,"cta":11781,"date":11784,"description":11785,"extension":116,"image":11786,"lastUpdated":6203,"meta":11787,"navigation":129,"path":11793,"seo":11794,"sitemap":11795,"stem":11796,"subtitle":11797,"tags":11798,"tldr":11800,"video":3,"__hash__":11801},"blog\u002Fblog\u002F2024\u002F08\u002Fopc-ua-to-mqtt-with-node-red.md","Bridging OPC UA Data to MQTT with Node-RED (2026)",[144],{"type":12,"value":11306,"toc":11769},[11307,11310,11313,11317,11326,11329,11332,11341,11345,11348,11350,11377,11381,11384,11398,11406,11414,11423,11430,11438,11473,11476,11480,11483,11501,11509,11549,11553,11556,11565,11573,11589,11597,11654,11658,11661,11664,11696,11704,11731,11733],[15,11308,11309],{},"Have you ever found yourself trying to connect old industrial systems with new IoT tools? This is a common scenario when trying to digitally transform while setting up your Unified Name Space. Maybe you have machinery that uses OPC UA, but your data is sent through MQTT. How do you make these systems work together smoothly?",[15,11311,11312],{},"In this guide, we'll demonstrate how to use Node-RED to bridge OPC UA data to MQTT. This integration will streamline your data flow and enhance real-time monitoring, helping you modernize your setup and improve communication between systems.",[399,11314,11316],{"id":11315},"why-bridge-opc-ua-to-mqtt","Why Bridge OPC UA to MQTT",[15,11318,11319,11323],{},[165,11320],{"alt":11321,"dataZoomable":103,"src":11322},"Diagram showing the data flow when bridging OPC UA to MQTT to enable communication between non-OPC UA compatible systems and devices","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopc-ua-to-mqtt.png",[170,11324,11325],{},"Diagram showing the data flow when bridging OPC UA to MQTT to enable communication between non-OPC UA compatible systems and devices.",[15,11327,11328],{},"In modern industrial environments, integrating systems with different communication protocols can be a significant challenge. For example, a CNC machine on the factory floor might use OPC UA, while some cloud solutions, edge devices, and other systems, such as custom ERP solutions and IoT applications, might rely on MQTT protocol. This is where bridging OPC UA to MQTT becomes highly beneficial.",[15,11330,11331],{},"By converting OPC UA data into MQTT messages, you make the data from the CNC machine accessible to a broader range of systems that use MQTT, which is a more universally supported messaging protocol. This bridging solution simplifies the integration process, allowing diverse systems to communicate effectively without needing direct OPC UA support.",[15,11333,11334,11336,11337,29],{},[19,11335,811],{}," is perfect for this job. It can connect both OPC UA and MQTT, making it easy to transform and route data between different systems. Its flexibility and support for many protocols make it great for integrating various industrial hardware and software. For more on how Node-RED can improve industrial operations, check out ",[63,11338,11340],{"href":11339},"\u002Fblog\u002F2024\u002F07\u002Fbuilding-on-flowfuse-devices\u002F","Building on FlowFuse: Remote Device Monitoring",[31,11342,11344],{"id":11343},"bridging-opc-ua-data-to-mqtt-with-node-red","Bridging OPC UA Data to MQTT with Node-RED",[15,11346,11347],{},"In this section, I'll demonstrate how to bridge OPC UA data to MQTT using Node-RED. We will use simulated OPC UA server data from a CNC machine as an example. The goal is to show how you can efficiently transfer this data to an MQTT broker, making it accessible to various applications and systems.",[399,11349,6240],{"id":6239},[39,11351,11352,11359,11362,11370],{},[42,11353,11354,11355,29],{},"OPC UA Server: Make sure you have an OPC UA server configured and running with the necessary data. For this blog, we'll use the Prosys OPC UA Simulation Server, which simulates data from CNC machines designed for testing OPC UA client applications and learning the technology. You can download it from ",[63,11356,3986],{"href":11357,"rel":11358},"https:\u002F\u002Fprosysopc.com\u002Fproducts\u002Fopc-ua-simulation-server\u002F",[67],[42,11360,11361],{},"FlowFuse Account: A FlowFuse account lets you quickly create, deploy, and manage Node-RED instances in the cloud. [sign up now]({% include \"sign-up-url.njk\" %}?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Bridging%20OPC%20UA%20Data%20to%20MQTT%20with%20Node-RED).",[42,11363,11364,11369],{},[63,11365,11368],{"href":11366,"rel":11367},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-opcua",[67],"node-red-contrib-opcua",": install the node-red contrib package that will enable integration of opcua in Node-RED.",[42,11371,11372,11373,29],{},"MQTT Broker: We’ll need an MQTT broker for data communication. FlowFuse offers an integrated MQTT Broker Service within Platform for easy setup. For more details, check out ",[63,11374,11376],{"href":11375},"\u002Fblog\u002F2024\u002F10\u002Fannouncement-mqtt-broker\u002F","FlowFuse's MQTT Broker Announcement",[399,11378,11380],{"id":11379},"retrieving-data-from-the-opc-ua-server","Retrieving Data from the OPC UA Server",[15,11382,11383],{},"To begin retrieving data from your OPC UA server using Node-RED, follow these steps:",[271,11385,11386,11390],{},[42,11387,5054,11388,1216],{},[19,11389,1588],{},[42,11391,5054,11392,11394,11395,11397],{},[19,11393,9518],{}," node onto the canvas and double-click on the node to open its configuration settings. Set the ",[924,11396,1241],{}," to the node ID and datatype of the property you wish to read.",[15,11399,11400,11404],{},[165,11401],{"alt":11402,"dataZoomable":103,"src":11403},"(Left) Image of the Change node setting the 'msg.topic' to retrieve the cycle time data and (Right) the OPC UA Prosys interface.","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fchange-node-setting-nodeid-datatype.png",[170,11405,11402],{},[271,11407,11408],{"start":107},[42,11409,5054,11410,11413],{},[19,11411,11412],{},"OpcUa-Client"," node onto the canvas. Double-click on it to open its configuration settings. Click the \"+\" icon next to the Endpoint field and enter the URL of your running OPC UA server. Configure the security policy and mode according to your server setup. If you use the Prosys OPC UA Simulation Server and have not enabled any security features, you can leave the security policy and mode as \"None.\"",[15,11415,11416,11420],{},[165,11417],{"alt":11418,"dataZoomable":103,"src":11419},"Configuring opc-ua client node with the opc ua server endpoint","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopc-ua-config.png",[170,11421,11422],{},"Configuring opc-ua node with the opc ua server endpoint",[271,11424,11425],{"start":294},[42,11426,1654,11427,11429],{},[19,11428,11412],{}," node settings, select the action type as \"READ.\" This instructs Node-RED to read data from the OPC UA server.",[15,11431,11432,11436],{},[165,11433],{"alt":11434,"dataZoomable":103,"src":11435},"Configuring OpcUa-Client node to select the read operation","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopc-ua-config2.png",[170,11437,11434],{},[271,11439,11440,11443,11448,11470],{"start":104},[42,11441,11442],{},"If your OPC UA server uses security features, specify the path to your certificate files in the relevant fields. If no security is configured, this step can be skipped.",[42,11444,5054,11445,11447],{},[19,11446,1634],{}," node onto the canvas. The output will help you verify the data retrieved from the OPC UA server.",[42,11449,8798,11450,8801,11452,8804,11454,8801,11456,11458,11459,8801,11461,11463,11464,11466,11467,11469],{},[19,11451,1588],{},[19,11453,9518],{},[19,11455,9518],{},[19,11457,11412],{}," node. Then, connect the output of the ",[19,11460,11412],{},[19,11462,1634],{}," node. This setup ensures that when the ",[19,11465,1588],{}," node triggers, it sends data to the ",[19,11468,11412],{}," node, and the results are displayed in the Debug node.",[42,11471,11472],{},"Deploy the flow by clicking the \"Deploy\" button in the top right corner. To test the setup, press the Inject button.",[15,11474,11475],{},"You can follow the same steps to retrieve other property values from the OPC UA server. In this example, we are retrieving four simulated data properties: the cycle time, temperature, and spindle speed of the simulated CNC machine. Your setup might differ depending on the properties and data available on your OPC UA server.",[399,11477,11479],{"id":11478},"transforming-and-aggregating-data","Transforming and Aggregating Data",[15,11481,11482],{},"Once you have successfully retrieved data from your OPC UA server, the next step is to transform and aggregate this data to make it suitable for publishing to an MQTT broker. This demonstration, we will aggregate the retrieved individual property values into a single object. Depending on your specific needs, you might choose to split the object properties and send them separately or perform various calculations and transformations on the data.",[271,11484,11485,11489],{},[42,11486,5054,11487,1216],{},[19,11488,9518],{},[42,11490,11491,11492,11494,11495,1242,11497,11500],{},"Double-click on the node and set ",[924,11493,1241],{}," to the name of the property you want to set for the retrieved data. In this context, set ",[924,11496,1241],{},[924,11498,11499],{},"'cycle-time'",", which will be the key in the object that we will create.",[15,11502,11503,11506],{},[165,11504],{"alt":11505,"dataZoomable":103,"src":11403},"Setting the msg.topic with the Change node to retrieve data from the OPC UA server.",[170,11507,11508],{},"Setting the msg.topic with the change node to retrieve data from the OPC UA server.",[271,11510,11511,11526,11540,11546],{"start":294},[42,11512,5054,11513,11516,11517,11519,11520,11522,11523,11525],{},[19,11514,11515],{},"join"," node onto the canvas. Set the mode to manual, with the option to create ",[924,11518,1227],{}," using the values of ",[924,11521,1241],{}," as keys. Set the count to 3 and ensure that the interval for all of the ",[19,11524,1588],{}," nodes triggering data retrieval is the same. This ensures that the data is collected and aggregated correctly at the same time.",[42,11527,8798,11528,11530,11531,11533,11534,11536,11537,11539],{},[19,11529,11412],{}," node (which retrieves the data) to the input of the ",[19,11532,9518],{}," node. For example, if I have set the ",[19,11535,9518],{}," node for the 'cycle-time' data property, connect it to the ",[19,11538,11412],{}," node that retrieves this data.",[42,11541,8798,11542,8801,11544,1318],{},[19,11543,9518],{},[19,11545,11515],{},[42,11547,11548],{},"Repeat this process for all of your data properties.",[399,11550,11552],{"id":11551},"sending-data-to-the-mqtt-broker","Sending Data to the MQTT Broker",[15,11554,11555],{},"Now, in this section, we will show you how to send the collected data to an MQTT broker:",[271,11557,11558,11562],{},[42,11559,5054,11560,1216],{},[19,11561,7632],{},[42,11563,11564],{},"Double-click on it and configure it with your MQTT broker details.",[15,11566,11567,11571],{},[165,11568],{"alt":11569,"dataZoomable":103,"src":11570},"Configuring the mqtt out node with broker information","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fmqtt-out-node-config.png",[170,11572,11569],{},[271,11574,11575,11580,11586],{"start":294},[42,11576,11577,11578,1318],{},"Set the topic for your data in the ",[19,11579,7632],{},[42,11581,8798,11582,8801,11584,1318],{},[19,11583,11515],{},[19,11585,7632],{},[42,11587,11588],{},"Deploy the flow. After deploying, you will see the status \"connected\" with a green dot at the bottom of each node, indicating that you have successfully connected to your MQTT broker.",[15,11590,11591,11595],{},[165,11592],{"alt":11593,"dataZoomable":103,"src":11594},"Image showing the successful bridging of OPC UA data to MQTT","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fopcua-to-mqtt.gif",[170,11596,11593],{},[15,11598,4917,11599,1150],{},[1107,11600,11601,11602,11604,11605,11607,11608,11610,11611,11613,11614,11617,11618,11621,11622,11625,11626,11628,11629,11631,11632,11634,11635,11638,11639,11642,11643,11646,11647,11649,11650],{},"{\"id\":\"a099aefb08837e70\",\"type\":\"OpcUa-Client\",\"z\":\"807758ec576fbfd8\",\"endpoint\":\"9dd56eda04f5c5b5\",\"action\":\"read\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"\",\"x\":480,\"y\":320,\"wires\":[[\"f5fd1ffafdfe790f\"],",[1107,11603],{},"]},{\"id\":\"1aa02b27b99dfe9d\",\"type\":\"mqtt out\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"topic\":\"\u002Fmanufacturing\u002Fcnc\",\"qos\":\"2\",\"retain\":\"true\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"abd4e6202945fee3\",\"x\":1390,\"y\":380,\"wires\":",[1107,11606],{},"},{\"id\":\"d565ae620d90498a\",\"type\":\"OpcUa-Client\",\"z\":\"807758ec576fbfd8\",\"endpoint\":\"9dd56eda04f5c5b5\",\"action\":\"read\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"\",\"x\":480,\"y\":400,\"wires\":[[\"fc4b83a8a0be3a35\"],",[1107,11609],{},"]},{\"id\":\"0e0614ada3269627\",\"type\":\"OpcUa-Client\",\"z\":\"807758ec576fbfd8\",\"endpoint\":\"9dd56eda04f5c5b5\",\"action\":\"read\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"\",\"x\":480,\"y\":480,\"wires\":[[\"e1c4fe72e4f37b6a\"],",[1107,11612],{},"]},{\"id\":\"f5fd1ffafdfe790f\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"Set the topic for the data\",\"rules\":",[1107,11615,11616],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"cycle-time\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":730,\"y\":300,\"wires\":[[\"913e9de1324a6f21\"]]},{\"id\":\"fc4b83a8a0be3a35\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"Set the topic for the data\",\"rules\":",[1107,11619,11620],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"spindle-speed\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":730,\"y\":380,\"wires\":[[\"913e9de1324a6f21\"]]},{\"id\":\"e1c4fe72e4f37b6a\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"Set the topic for the data\",\"rules\":",[1107,11623,11624],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"temperature\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":730,\"y\":460,\"wires\":[[\"913e9de1324a6f21\"]]},{\"id\":\"913e9de1324a6f21\",\"type\":\"join\",\"z\":\"807758ec576fbfd8\",\"name\":\"Create object from those three data property \",\"mode\":\"custom\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\n\",\"joinerType\":\"str\",\"useparts\":false,\"accumulate\":false,\"timeout\":\"\",\"count\":\"3\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":1080,\"y\":380,\"wires\":[[\"1aa02b27b99dfe9d\"]]},{\"id\":\"3339483f64116fce\",\"type\":\"mqtt in\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"topic\":\"\u002Fmanufacturing\u002Fcnc\",\"qos\":\"2\",\"datatype\":\"auto-detect\",\"broker\":\"abd4e6202945fee3\",\"nl\":false,\"rap\":true,\"rh\":0,\"inputs\":0,\"x\":170,\"y\":660,\"wires\":[[\"d881d251071bd317\"]]},{\"id\":\"d881d251071bd317\",\"type\":\"debug\",\"z\":\"807758ec576fbfd8\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":460,\"y\":660,\"wires\":",[1107,11627],{},"},{\"id\":\"55f43460af9601ec\",\"type\":\"comment\",\"z\":\"807758ec576fbfd8\",\"name\":\"Retrieving the data from mqtt\",\"info\":\"\",\"x\":320,\"y\":600,\"wires\":",[1107,11630],{},"},{\"id\":\"628ab54495901021\",\"type\":\"comment\",\"z\":\"807758ec576fbfd8\",\"name\":\"Bridging OPC UA data to MQTT\",\"info\":\"\",\"x\":350,\"y\":240,\"wires\":",[1107,11633],{},"},{\"id\":\"ce6b8a0e2c8a895d\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"rules\":",[1107,11636,11637],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"ns=3;i=1010,datatype=float\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":290,\"y\":320,\"wires\":[[\"a099aefb08837e70\"]]},{\"id\":\"50fe2b9310d3bb3f\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"rules\":",[1107,11640,11641],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"ns=3;i=1011,datatype=basedatatype\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":290,\"y\":400,\"wires\":[[\"d565ae620d90498a\"]]},{\"id\":\"9cf691f55748d013\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"rules\":",[1107,11644,11645],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"ns=3;i=1012,datatype=float\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":290,\"y\":480,\"wires\":[[\"0e0614ada3269627\"]]},{\"id\":\"de74dabc616c3094\",\"type\":\"inject\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"props\":",[1107,11648,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":120,\"y\":400,\"wires\":[[\"ce6b8a0e2c8a895d\",\"50fe2b9310d3bb3f\",\"9cf691f55748d013\"]]},{\"id\":\"9dd56eda04f5c5b5\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp:\u002F\u002FRoni:53530\u002FOPCUA\u002FSimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"abd4e6202945fee3\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"",[63,11651,11653],{"rel":11652},[67],"http:\u002F\u002Fbroker.hivemq.com\",\"port\":\"1883\",\"clientid\":\"\",\"autoConnect\":true,\"usetls\":false,\"protocolVersion\":\"4\",\"keepalive\":\"60\",\"cleansession\":true,\"autoUnsubscribe\":true,\"birthTopic\":\"\",\"birthQos\":\"0\",\"birthRetain\":\"false\",\"birthPayload\":\"\",\"birthMsg\":{},\"closeTopic\":\"\",\"closeQos\":\"0\",\"closeRetain\":\"false\",\"closePayload\":\"\",\"closeMsg\":{},\"willTopic\":\"\",\"willQos\":\"0\",\"willRetain\":\"false\",\"willPayload\":\"\",\"willMsg\":{},\"userProps\":\"\",\"sessionExpiry\":\"\"}",[31,11655,11657],{"id":11656},"bridging-mqtt-data-to-opc-ua","Bridging MQTT Data to OPC UA",[15,11659,11660],{},"In addition to bridging data from OPC UA to MQTT, you might also need to send data from MQTT back to an OPC UA server. This is often required in scenarios where external systems, such as Manufacturing Execution Systems (MES), need to update or control machinery settings.",[15,11662,11663],{},"For example, an MES can send commands or configuration changes via MQTT, which then need to be applied to an OPC UA-controlled machine.",[271,11665,11666,11672,11680,11685],{},[42,11667,1045,11668,11671],{},[19,11669,11670],{},"mqtt in"," node onto the Node-RED canvas and configure it with your MQTT broker details and the appropriate topic where the MES publishes commands.",[42,11673,5054,11674,11676,11677,11679],{},[19,11675,9518],{}," node onto the canvas, Set the ",[924,11678,1241],{}," to the node ID and datatype of the property you wish to update.",[42,11681,4744,11682,11684],{},[19,11683,11412],{}," node to the canvas and configure it with your OPC UA server. Set the action type to \"WRITE\" to send the received data.",[42,11686,8798,11687,8801,11689,11691,11692,8801,11694,1318],{},[19,11688,11670],{},[19,11690,9518],{}," node, and the output of the ",[19,11693,9518],{},[19,11695,11412],{},[15,11697,11698,11702],{},[165,11699],{"alt":11700,"dataZoomable":103,"src":11701},"Image showing the successful bridging of MQTT data to OPC UA","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fmqtt-to-opcua.gif",[170,11703,11593],{},[15,11705,4917,11706,1150],{},[1107,11707,11708,11709,11711,11712,11714,11715,11718,11719,11721,11722,11724,11725,11727,11728],{},"{\"id\":\"a099aefb08837e70\",\"type\":\"OpcUa-Client\",\"z\":\"FFF0000000000001\",\"endpoint\":\"9dd56eda04f5c5b5\",\"action\":\"write\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"\",\"x\":760,\"y\":220,\"wires\":[[],",[1107,11710],{},"]},{\"id\":\"628ab54495901021\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Bridging MQTT to OPC UA\",\"info\":\"\",\"x\":510,\"y\":140,\"wires\":",[1107,11713],{},"},{\"id\":\"ce6b8a0e2c8a895d\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"rules\":",[1107,11716,11717],{},"{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"ns=3;i=1010,datatype=Boolean\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"paylad\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":540,\"y\":220,\"wires\":[[\"a099aefb08837e70\"]]},{\"id\":\"0a89ebd0d9f6f577\",\"type\":\"mqtt in\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"topic\":\"command\u002Fcnc\u002F\",\"qos\":\"2\",\"datatype\":\"auto-detect\",\"broker\":\"abd4e6202945fee3\",\"nl\":false,\"rap\":true,\"rh\":0,\"inputs\":0,\"x\":300,\"y\":220,\"wires\":[[\"ce6b8a0e2c8a895d\"]]},{\"id\":\"4cf4e425d075722a\",\"type\":\"mqtt out\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"topic\":\"command\u002Fcnc\u002F\",\"qos\":\"1\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"abd4e6202945fee3\",\"x\":660,\"y\":400,\"wires\":",[1107,11720],{},"},{\"id\":\"8e339e511c573905\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,11723,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"false\",\"payloadType\":\"bool\",\"x\":330,\"y\":400,\"wires\":[[\"4cf4e425d075722a\"]]},{\"id\":\"9c33cff54b0aca15\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Sending Command\",\"info\":\"\",\"x\":490,\"y\":340,\"wires\":",[1107,11726],{},"},{\"id\":\"9dd56eda04f5c5b5\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp:\u002F\u002FRoni:53530\u002FOPCUA\u002FSimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"abd4e6202945fee3\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"",[63,11729,11653],{"rel":11730},[67],[399,11732,4189],{"id":4188},[39,11734,11735,11742,11749,11755,11762],{},[42,11736,11737,11741],{},[63,11738,11740],{"href":11739},"\u002Fnode-red\u002Fprotocol\u002Fmqtt\u002F","Using MQTT with Node-RED","\nLearn how to integrate MQTT with Node-RED to enhance your IoT solutions with real-time data messaging.",[42,11743,11744,11748],{},[63,11745,11747],{"href":11746},"\u002Fblog\u002F2023\u002F07\u002Fhow-to-build-a-opc-client-dashboard-in-node-red\u002F","How to Build an OPC UA Client Dashboard in Node-RED","\nFollow a step-by-step guide to create a comprehensive OPC UA client dashboard in Node-RED for effective monitoring and control.",[42,11750,11751,11754],{},[63,11752,11753],{"href":2640},"Building a Secure OPC UA Server in Node-RED","\nExplore best practices for configuring a secure OPC UA server in Node-RED to ensure safe and reliable data exchange.",[42,11756,11757,11761],{},[63,11758,11760],{"href":11759},"\u002Fblog\u002F2023\u002F07\u002Fhow-to-deploy-a-basic-opc-ua-server-in-node-red\u002F","How to Deploy a Basic OPC UA Server in Node-RED","\nLearn how to quickly deploy a basic OPC UA server in Node-RED for testing and development purposes.",[42,11763,11764,11768],{},[63,11765,11767],{"href":11766},"\u002Fblog\u002F2023\u002F06\u002Fnode-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter\u002F","Node-RED as a No-Code EtherNet\u002FIP to S7 Protocol Converter","\nDiscover how to use Node-RED to seamlessly convert EtherNet\u002FIP to S7 protocols with Node-RED.",{"title":103,"searchDepth":104,"depth":104,"links":11770},[11771,11772,11778],{"id":11315,"depth":294,"text":11316},{"id":11343,"depth":107,"text":11344,"children":11773},[11774,11775,11776,11777],{"id":6239,"depth":294,"text":6240},{"id":11379,"depth":294,"text":11380},{"id":11478,"depth":294,"text":11479},{"id":11551,"depth":294,"text":11552},{"id":11656,"depth":107,"text":11657,"children":11779},[11780],{"id":4188,"depth":294,"text":4189},{"type":748,"title":11782,"description":11783},"Connect OPC UA and MQTT with Node-RED on FlowFuse","FlowFuse makes it easy to deploy Node-RED in the cloud or at the edge and bridge industrial protocols like OPC UA and MQTT, with built-in MQTT broker support, team collaboration, and production-ready infrastructure.","2024-08-13","Learn how to bridge OPC UA data to MQTT using Node-RED for seamless industrial IoT integration and real-time data flow.","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fbrdging-opcua-to-mqtt.png",{"keywords":11788,"excerpt":11789},"opc ua gateway, opc ua example, node-red-contrib-opcua, nodered opcua, opc ua mqtt, opc ua over mqtt, opc ua to mqtt gateway, mqtt to opc ua, mqtt and opc ua, opc ua and mqtt, opc ua via mqtt, opcua to mqtt",{"type":12,"value":11790},[11791],[15,11792,11309],{},"\u002Fblog\u002F2024\u002F08\u002Fopc-ua-to-mqtt-with-node-red",{"title":11303,"description":11785},{"loc":11793},"blog\u002F2024\u002F08\u002Fopc-ua-to-mqtt-with-node-red","Connecting OPC UA Data Streams to MQTT Brokers for Enhanced IoT Communication and Monitoring",[8650,138,8410,11799,7457],"opcua","This guide demonstrates how to connect an OPC UA server to an MQTT broker using Node-RED, allowing industrial machine data to flow into modern IoT systems. It covers installing node-red-contrib-opcua, reading multiple OPC UA node values, aggregating them with a join node, and publishing the result to MQTT. A reverse bridging pattern for sending MQTT commands back to OPC UA is also covered.","1ykI1HGoZ2_Ok3cOEeLGGBNC4WqOMHPU3pfFFRiCFww",{"id":11803,"title":11804,"authors":11805,"body":11806,"cta":12613,"date":12616,"description":12617,"extension":116,"image":12618,"lastUpdated":6064,"meta":12619,"navigation":129,"path":12625,"seo":12626,"sitemap":12627,"stem":12628,"subtitle":12629,"tags":12630,"tldr":12633,"video":3,"__hash__":12634},"blog\u002Fblog\u002F2024\u002F08\u002Fcomparing-dashboard-2-with-uibuilder.md","FlowFuse Dashboard vs UI-Builder: A Comprehensive Comparison",[144],{"type":12,"value":11807,"toc":12545},[11808,11811,11815,11818,11825,11829,11836,11840,11843,11846,11849,11852,11856,11868,11871,11874,11882,11885,11888,11891,11895,11898,11905,11916,11919,11926,11937,11941,11944,11947,11950,11953,11957,11960,11967,11970,11973,11977,11980,11994,11997,12000,12004,12007,12010,12013,12016,12019,12023,12026,12035,12038,12041,12045,12048,12051,12059,12062,12065,12068,12072,12075,12084,12088,12091,12095,12098,12101,12104,12107,12111,12114,12117,12120,12123,12127,12130,12133,12142,12145,12148,12157,12161,12164,12167,12170,12173,12177,12180,12189,12198,12201,12204,12207,12211,12537,12539,12542],[15,11809,11810],{},"When choosing a dashboard solution for Node-RED, two popular options are FlowFuse Dashboard (also known as Dashboard 2.0) and UI-Builder. This article compares these tools across several key areas, including installation, ease of use, development activity, and customizability, to help you decide which one best suits your needs.",[31,11812,11814],{"id":11813},"how-easy-is-it-to-install","How Easy is it to Install?",[399,11816,3465],{"id":11817},"flowfuse-dashboard",[15,11819,11820,11821,11824],{},"When searching for ",[924,11822,11823],{},"flowfuse\u002Fnode-red-dashboard"," on Google, the first result we get to the documentation, which is useful. However, in the Node-RED Palette Manager, finding the correct package can be confusing because there are many community third-party nodes and plugins that work with FlowFuse Dashboard. This can make it bit confusing for new users to locate the right package.",[399,11826,11828],{"id":11827},"ui-builder","UI-Builder",[15,11830,11831,11832,11835],{},"Finding UI-Builder is straightforward, as its package name ",[924,11833,11834],{},"node-red-contrib-uibuilder"," is distinct and easily identifiable both on Google and in the Node-RED Palette Manager. This clear naming helps users quickly locate the correct package.",[31,11837,11839],{"id":11838},"how-easy-is-it-to-get-started","How Easy is it to Get Started?",[399,11841,3465],{"id":11842},"flowfuse-dashboard-1",[15,11844,11845],{},"Getting started with FlowFuse Dashboard is relatively easy due to its low-code approach. It features a sidebar for managing UI widgets, themes, configurations, and settings, with intuitive navigation to the dashboard page. This makes it accessible for users with varying levels of technical expertise.",[399,11847,11828],{"id":11848},"ui-builder-1",[15,11850,11851],{},"UI-Builder can be more challenging to start with, as it does not follow a low-code approach and lacks a separate sidebar for managing UI elements. Navigating dashboards built with UI-Builder can be less straightforward, and it requires users to have a deeper understanding of coding and UI design principles.",[31,11853,11855],{"id":11854},"migrating-from-node-red-dashboard-dashboard-10","Migrating from Node-RED Dashboard (Dashboard 1.0)",[15,11857,11858,11863,11864,29],{},[63,11859,11862],{"href":11860,"rel":11861},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-dashboard",[67],"Node-RED Dashboard"," is a module that provides a set of nodes in Node-RED to quickly create user interfaces or live data dashboards. It was developed by one of the creators of Node-RED and is the most used and downloaded package in the Node-RED ecosystem. However, it is now deprecated. For more information, refer to ",[63,11865,11867],{"href":11866},"\u002Fblog\u002F2024\u002F06\u002Fdashboard-1-deprecated\u002F","Node-RED Dashboard Formally Deprecated",[399,11869,3465],{"id":11870},"flowfuse-dashboard-2",[15,11872,11873],{},"FlowFuse Dashboard is developed to replace the the deprecated standard Node-RED Dashboard. It retains the core concepts and UI elements but introduces more advanced options and configurations.",[15,11875,11876,11877,11881],{},"To facilitate the transition, FlowFuse, the creator of FlowFuse Dashboard, provides a ",[63,11878,11880],{"href":11879},"\u002Fplatform\u002Fdashboard\u002F#migration-service","migration service"," that simplifies migration of flows or projects from the Node-RED Dashboard to FlowFuse Dashboard. This service helps ensure a smoother migration process with minimal disruption.",[399,11883,11828],{"id":11884},"ui-builder-2",[15,11886,11887],{},"Migrating from Node-RED Dashboard 1 to UI-Builder is significantly more complex. UI-Builder does not follow the same concepts or provide the same UI elements as the Node-RED Dashboard.",[15,11889,11890],{},"Users will need to recreate their dashboards from scratch, as UI-Builder relies on custom coding and frontend frameworks rather than the predefined, low-code widgets of Node-RED Dashboard. This process can be overwhelming and requires a solid understanding of HTML, CSS and the frontend frameworks if you wanted use.",[31,11892,11894],{"id":11893},"how-active-is-the-projects-development","How Active is the Project's Development?",[399,11896,3465],{"id":11897},"flowfuse-dashboard-3",[15,11899,11900,11904],{},[63,11901,3465],{"href":11902,"rel":11903},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fgraphs\u002Fcontributors",[67],", which replaced Node-RED Dashboard 1.0 in 2023, has shown consistent and high development activity. The project benefits from a dedicated team that regularly updates and improves it, ensuring it remains current with user needs and technological advancements.",[15,11906,11907,11911,11913],{},[165,11908],{"alt":11909,"src":11910},"Screenshot of the FlowFuse Dashboard GitHub commit chart","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fdashboard-2-commits.png",[5368,11912],{},[170,11914,11915],{},"Screenshot of the FlowFuse Dashboard GitHub commit chart.",[399,11917,11828],{"id":11918},"ui-builder-3",[15,11920,11921,11925],{},[63,11922,11828],{"href":11923,"rel":11924},"https:\u002F\u002Fgithub.com\u002FTotallyInformation\u002Fnode-red-contrib-uibuilder\u002Fgraphs\u002Fcontributors",[67]," has been an active project for a long time and remains active. However, there has been a noticeable decline in development activity starting in early 2024. While the project continues to be maintained, it does not have a dedicated, full-time team.",[15,11927,11928,11932,11934],{},[165,11929],{"alt":11930,"src":11931},"Screenshot of the UI-Builder GitHub commit chart","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fui-builder-commits.png",[5368,11933],{},[170,11935,11936],{},"Screenshot of the UI-Builder GitHub commit chart.",[31,11938,11940],{"id":11939},"pre-built-ui-elements","Pre-Built UI Elements",[399,11942,3465],{"id":11943},"flowfuse-dashboard-4",[15,11945,11946],{},"FlowFuse Dashboard offers an extensive set of UI elements, including forms, dropdowns, tables, charts, and gauges that are super easy to use. These widgets are built with complex Vue.js components, but users are completely shielded from this complexity, allowing them to focus on ease of use. Additionally, if you want to create your own custom widget, you can do so with the ui-template node that accepts complete Vue components. By default, FlowFuse Dashboard supports the Vuetify library, which provides an extensive set of UI components.",[399,11948,11828],{"id":11949},"ui-builder-4",[15,11951,11952],{},"UI-Builder also offers a number of widgets, but these are not as user-friendly as those in FlowFuse Dashboard. Users must send a JSON config object, which can be complex for new users who lack knowledge of HTML\u002FCSS. Additionally, handling widget data requires using UI-Builder's methods, which can further increase complexity. However, UI-Builder's strength lies in its flexibility, allowing any HTML element to be used as a component, and it also supports the W3C standard web components. Despite this, users need to perform a lot of additional tasks to get everything set up and will have the hassle of writing things.",[31,11954,11956],{"id":11955},"changing-the-ui-at-runtime","Changing the UI at Runtime",[399,11958,3465],{"id":11959},"flowfuse-dashboard-5",[15,11961,11962,11963,11966],{},"FlowFuse Dashboard supports dynamic control of the UI via the msg object. Each UI widget supports the ",[924,11964,11965],{},"msg.ui_update"," property, which allows you to control and update UI components dynamically. For example, you can update form fields based on user input, dynamically insert or update options in a dropdown, or change the appearance of the UI by sending CSS classes or selecting options in dropdowns or radio buttons through msg.",[399,11968,11828],{"id":11969},"ui-builder-5",[15,11971,11972],{},"UI-Builder also supports dynamic UI updates and control. Similar to FlowFuse Dashboard, you can use messages to control the state and content of UI elements.",[31,11974,11976],{"id":11975},"data-visualization","Data Visualization",[399,11978,3465],{"id":11979},"flowfuse-dashboard-6",[15,11981,11982,11983,1413,11988,11993],{},"Monitoring devices is one of the core use cases for Node-RED, and FlowFuse Dashboard makes it easy to monitor device metrics with its built-in ",[63,11984,11987],{"href":11985,"rel":11986},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-chart.html",[67],"chart",[63,11989,11992],{"href":11990,"rel":11991},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-gauge.html",[67],"gauge widgets",". The chart widgets support various types, including line, bar, scatter, pie, and donut charts, while the gauge widgets offer options like tile, 3\u002F4 gauge, and half gauge. This range of built-in options simplifies the creation of visualizations for monitoring device metrics. Additionally, you can use Vuetify or other third-party libraries in the ui-template node if you need different types of charts.",[399,11995,11828],{"id":11996},"ui-builder-6",[15,11998,11999],{},"UI-Builder does not provide built-in charting options. However, you can add charts using third-party JavaScript libraries. This approach requires additional effort to integrate and configure the libraries, as well as writing the relevant JavaScript to render the charts. While this increases the complexity of creating visualizations, it offers more control and customization.",[31,12001,12003],{"id":12002},"building-layout","Building Layout",[399,12005,3465],{"id":12006},"flowfuse-dashboard-7",[15,12008,12009],{},"FlowFuse Dashboard offers a collection of pre-defined layouts to make it easy for users to get started quickly. These are available as a configuration for each \"Page\" of your application.",[15,12011,12012],{},"These predefined layouts provide a solid foundation for most applications. If you need a different layout, FlowFuse Dashboard is limited in it's customization in terms of positioning of elements.",[399,12014,11828],{"id":12015},"ui-builder-7",[15,12017,12018],{},"UI-Builder does not come with predefined layouts, which can make it more complex for users to get started. it does however, provides the flexibility to completely define your own layout using CSS. This approach allows for complete customization, but it requires users to have a good understanding of HTML and CSS. The lack of predefined layouts means users have to create their own from scratch, which can be time-consuming.",[31,12020,12022],{"id":12021},"responsiveness-on-mobile","Responsiveness on Mobile",[399,12024,3465],{"id":12025},"flowfuse-dashboard-8",[15,12027,12028,12029,12034],{},"FlowFuse Dashboard is designed with ",[63,12030,12033],{"href":12031,"rel":12032},"https:\u002F\u002Fdashboard.flowfuse.com\u002Flayouts\u002F",[67],"responsiveness"," in mind. It ensures that dashboards automatically adapt to different screen sizes and devices, providing a consistent user experience across desktops, tablets, and mobile devices. The dashboard elements are automatically adjusted to fit various resolutions, making it user-friendly for a wide audience.",[399,12036,11828],{"id":12037},"ui-builder-8",[15,12039,12040],{},"UI-Builder's support for different devices depends on the user's implementation. While it offers the potential for responsive designs, achieving this requires a good understanding of responsive design principles and CSS.",[31,12042,12044],{"id":12043},"how-much-customization-is-available","How Much Customization is Available?",[399,12046,3465],{"id":12047},"flowfuse-dashboard-9",[15,12049,12050],{},"FlowFuse Dashboard, built on Vue.js, offers a range of customization options through its widget configurations and settings.",[15,12052,12053,12054,12058],{},"While it provides predefined UI elements, users can override CSS and theming using the ",[63,12055,12057],{"href":12056},"\u002Fblog\u002F2023\u002F12\u002Fdashboard-0-10-0\u002F","ui_template"," widget to enhance the dashboard's appearance.",[15,12060,12061],{},"This same widget also provides functionality to develop custom components or widgets for your Dashboard. These must be built with a VueJS core, but do support the integration of third-party JavaScript libraries.",[399,12063,11828],{"id":12064},"ui-builder-9",[15,12066,12067],{},"UI-Builder provides extensive customization capabilities, enabling users to build and style UI elements from scratch. This flexibility is advantageous for those with a strong background in frontend development, as it supports any frontend framework or custom design approach. However, achieving the desired results requires significant time and expertise, as users need to handle the coding and styling for each UI element they create.",[31,12069,12071],{"id":12070},"can-it-be-installed-on-mobile","Can it be Installed on Mobile?",[399,12073,3465],{"id":12074},"flowfuse-dashboard-10",[15,12076,12077,12078,12083],{},"FlowFuse Dashboard is built and deployed as a Progressive Web App. This means whilst it's built as a web application, it can be ",[63,12079,12082],{"href":12080,"rel":12081},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fuser\u002Fpwa.html#installing-dashboards-on-mobile",[67],"installed on your mobile device"," and run as a standalone application, behaving as if it was a native app.",[399,12085,12087],{"id":12086},"ui-builder-10","Ui-Builder",[15,12089,12090],{},"Dashboards built with UI-Builder, on the other hand, cannot be installed as an app. It is designed as a web-based tool for building custom UIs and does not support installation as a standalone application.",[31,12092,12094],{"id":12093},"how-about-performance-and-speed","How About Performance and Speed?",[399,12096,3465],{"id":12097},"flowfuse-dashboard-11",[15,12099,12100],{},"FlowFuse Dashboard provides reliable performance with efficient real-time updates. Initial page load times might be slower, particularly on mobile, but the overall performance is consistent and effective for dynamic dashboards.",[399,12102,11828],{"id":12103},"ui-builder-11",[15,12105,12106],{},"UI-Builder provides faster initial load times and smooth performance across customizations, ensuring high-speed interactions.",[31,12108,12110],{"id":12109},"how-is-the-overall-user-experience","How is the Overall User Experience?",[399,12112,3465],{"id":12113},"flowfuse-dashboard-12",[15,12115,12116],{},"FlowFuse Dashboard offers a user-friendly experience with its low-code approach, intuitive UI, and extensive documentation. Users can quickly create and customize dashboards without needing advanced coding skills, making it accessible to a wide range of users, from beginners to experienced developers. This allows users to focus on solving business problems and IoT tasks rather than getting bogged down by complex coding requirements.",[399,12118,11828],{"id":12119},"ui-builder-12",[15,12121,12122],{},"UI-Builder provides a more flexible but complex user experience. While it allows for greater customization and the use of any frontend framework, it requires a solid understanding of coding and design principles. This can be daunting for users without a background in web development, but it offers powerful capabilities for those who are comfortable with custom coding.",[31,12124,12126],{"id":12125},"how-active-is-the-user-community","How Active is the User Community?",[399,12128,3465],{"id":12129},"flowfuse-dashboard-13",[15,12131,12132],{},"FlowFuse Dashboard boasts a large and active user community. Users frequently participate in forums, contribute to discussions, and share custom solutions. The number of weekly downloads is growing rapidly, reflecting its high activity and popularity.",[15,12134,12135,12139],{},[165,12136],{"alt":12137,"src":12138},"\"Screenshot of the FlowFuse Dashboard package's weekly download chart from npm\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fdashboard-2-download.png",[170,12140,12141],{},"Screenshot of the FlowFuse Dashboard package's weekly download chart from npm",[399,12143,11828],{"id":12144},"ui-builder-13",[15,12146,12147],{},"UI-Builder has a smaller but still active user community. While not as large as FlowFuse Dashboard's, it includes dedicated users who also engage in forums, contribute to discussions, and share their use cases and solutions.",[15,12149,12150,12154],{},[165,12151],{"alt":12152,"src":12153},"\"Screenshot of the Ui-Builder package's weekly download chart from npm\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fui-builder-downloads.png",[170,12155,12156],{},"Screenshot of the Ui-Builder package's weekly download chart from npm",[31,12158,12160],{"id":12159},"how-good-is-the-support-and-documentation","How Good is the Support and Documentation?",[399,12162,3465],{"id":12163},"flowfuse-dashboard-14",[15,12165,12166],{},"Support for FlowFuse Dashboard is robust, with assistance from both the FlowFuse team and the active Node-RED community. The documentation is comprehensive, easy to understand, and regularly updated. The team is also working on an interactive dashboard solution for previewing examples.",[399,12168,11828],{"id":12169},"ui-builder-14",[15,12171,12172],{},"UI-Builder also has good support, with active contributions from the author and the Node-RED community. The documentation is detailed but can be complex due to extensive use of technical language.",[31,12174,12176],{"id":12175},"what-are-the-future-development-plans","What are the future development plans?",[399,12178,3465],{"id":12179},"flowfuse-dashboard-15",[15,12181,12182,12183,12188],{},"FlowFuse Dashboard is actively working on enhancing its feature set. With extensive functionality already in place, the project also has ambitious plans to introduce more advanced features to better serve its users. You can track these developments on the ",[63,12184,12187],{"href":12185,"rel":12186},"https:\u002F\u002Fgithub.com\u002Forgs\u002FFlowFuse\u002Fprojects\u002F15\u002Fviews\u002F4",[67],"FlowFuse Dashboard GitHub project board",", where ongoing and upcoming features are documented.",[15,12190,12191,12195],{},[165,12192],{"alt":12193,"src":12194},"\"Screenshot of the FlowFuse Dashboard GitHub project board\"","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fdashboard-2-project-plan.png",[170,12196,12197],{},"Screenshot of the FlowFuse Dashboard GitHub project board",[15,12199,12200],{},"Additionally, updates about new features and enhancements are also provided through social media, blogs, and the Node-RED forums, ensuring that users stay informed about the latest developments.",[399,12202,11828],{"id":12203},"ui-builder-15",[15,12205,12206],{},"UI-Builder does not have a publicly accessible project roadmap or a dedicated planning board for future updates. While development continues, details about forthcoming features and enhancements are regularly updated on the Node-RED Discourse forums by its author.",[31,12208,12210],{"id":12209},"summary-table","Summary Table",[6376,12212,12213,12224],{},[6379,12214,12215],{},[6382,12216,12217,12220,12222],{},[6385,12218,12219],{},"Feature",[6385,12221,3465],{},[6385,12223,11828],{},[6406,12225,12226,12252,12270,12289,12307,12326,12343,12363,12371,12390,12409,12427,12445,12463,12481,12499,12518],{},[6382,12227,12228,12233,12242],{},[6411,12229,12230],{},[19,12231,12232],{},"Ease of Installation",[6411,12234,12235,12238,12239,12241],{},[19,12236,12237],{},"Moderately easy",": Search for ",[924,12240,11823],{}," can be confusing due to multiple nodes; useful documentation available",[6411,12243,12244,12247,12248,12251],{},[19,12245,12246],{},"Easy",": Clear package name ",[924,12249,12250],{},"node-red-ui-builder"," makes it straightforward to find",[6382,12253,12254,12259,12264],{},[6411,12255,12256],{},[19,12257,12258],{},"Ease of Getting Started",[6411,12260,12261,12263],{},[19,12262,12246],{},": Low-code interface with a sidebar for managing UI elements; intuitive for users of varying technical skills",[6411,12265,12266,12269],{},[19,12267,12268],{},"Challenging",": Requires understanding of coding and UI design; lacks a low-code approach",[6382,12271,12272,12277,12283],{},[6411,12273,12274],{},[19,12275,12276],{},"Migration from Node-RED Dashboard",[6411,12278,12279,12282],{},[19,12280,12281],{},"Smooth",": Migration service provided to transition from Node-RED Dashboard 1 to FlowFuse Dashboard",[6411,12284,12285,12288],{},[19,12286,12287],{},"Complex",": Requires rebuilding dashboards from scratch; significant effort needed for migration",[6382,12290,12291,12296,12301],{},[6411,12292,12293],{},[19,12294,12295],{},"Development Activity",[6411,12297,12298,12300],{},[19,12299,10335],{},": Regular updates and improvements; actively maintained by a dedicated team",[6411,12302,12303,12306],{},[19,12304,12305],{},"Moderate",": Active but shows a decline in updates; less frequent enhancements",[6382,12308,12309,12314,12320],{},[6411,12310,12311],{},[19,12312,12313],{},"UI Elements Collection",[6411,12315,12316,12319],{},[19,12317,12318],{},"Extensive",": Includes a wide range of widgets like forms, charts, and gauges; built on Vue.js with Vuetify support",[6411,12321,12322,12325],{},[19,12323,12324],{},"Flexible but complex",": Allows any HTML element; requires custom coding and handling of UI elements",[6382,12327,12328,12333,12338],{},[6411,12329,12330],{},[19,12331,12332],{},"Visualization",[6411,12334,12335,12337],{},[19,12336,12246],{},": Built-in charting options for various types; additional options with third-party libraries",[6411,12339,12340,12342],{},[19,12341,12287],{},": No built-in charts; requires integration of third-party libraries for visualization",[6382,12344,12345,12350,12358],{},[6411,12346,12347],{},[19,12348,12349],{},"Dynamic UI Control",[6411,12351,12352,12354,12355,12357],{},[19,12353,10299],{},": Can dynamically control and update UI components using ",[924,12356,11965],{}," property",[6411,12359,12360,12362],{},[19,12361,10299],{},": Dynamic updates possible, but can be more complex to implement",[6382,12364,12365,12367,12369],{},[6411,12366],{},[6411,12368],{},[6411,12370],{},[6382,12372,12373,12378,12384],{},[6411,12374,12375],{},[19,12376,12377],{},"Web Layout Support",[6411,12379,12380,12383],{},[19,12381,12382],{},"Predefined",": Offers three main layouts with some options for custom styling",[6411,12385,12386,12389],{},[19,12387,12388],{},"Customizable",": No predefined layouts; users must create their own using CSS",[6382,12391,12392,12397,12403],{},[6411,12393,12394],{},[19,12395,12396],{},"Device and Screen Size Support",[6411,12398,12399,12402],{},[19,12400,12401],{},"Excellent",": Responsive design adapts well to various devices and screen sizes",[6411,12404,12405,12408],{},[19,12406,12407],{},"Variable",": Responsiveness depends on user's implementation and knowledge of CSS",[6382,12410,12411,12416,12422],{},[6411,12412,12413],{},[19,12414,12415],{},"Customization",[6411,12417,12418,12421],{},[19,12419,12420],{},"Good",": Offers customization through widget settings and custom CSS and ui-template; supports Vue components and third-party libraries",[6411,12423,12424,12426],{},[19,12425,10335],{},": Full control over UI design and layout with custom coding; requires expertise in frontend development",[6382,12428,12429,12434,12440],{},[6411,12430,12431],{},[19,12432,12433],{},"App Installation",[6411,12435,12436,12439],{},[19,12437,12438],{},"Available",": Can be installed as a Progressive Web App (PWA) for use as a standalone application",[6411,12441,12442,12444],{},[19,12443,10296],{},": Designed as a web-based tool; cannot be installed as an app",[6382,12446,12447,12452,12458],{},[6411,12448,12449],{},[19,12450,12451],{},"Performance and Speed",[6411,12453,12454,12457],{},[19,12455,12456],{},"Reliable",": Good performance with consistent updates; may have slower initial load times on mobile",[6411,12459,12460,12462],{},[19,12461,12420],{},": Generally good performance on both desktop and mobile",[6382,12464,12465,12470,12476],{},[6411,12466,12467],{},[19,12468,12469],{},"Support and Documentation",[6411,12471,12472,12475],{},[19,12473,12474],{},"Robust",": Comprehensive, regularly updated documentation and active community support",[6411,12477,12478,12480],{},[19,12479,12420],{},": Detailed but may be complex; documentation includes technical jargon",[6382,12482,12483,12488,12494],{},[6411,12484,12485],{},[19,12486,12487],{},"Overall User Experience",[6411,12489,12490,12493],{},[19,12491,12492],{},"User-friendly",": Accessible and easy to use with low-code approach; suitable for a wide range of users",[6411,12495,12496,12498],{},[19,12497,12324],{},": Offers greater customization but requires coding expertise; can be daunting for non-developers",[6382,12500,12501,12506,12512],{},[6411,12502,12503],{},[19,12504,12505],{},"User Community Activity",[6411,12507,12508,12511],{},[19,12509,12510],{},"Large and active",": High engagement in forums and discussions; growing number of downloads",[6411,12513,12514,12517],{},[19,12515,12516],{},"Smaller but dedicated",": Active users, but less engagement compared to FlowFuse Dashboard",[6382,12519,12520,12525,12531],{},[6411,12521,12522],{},[19,12523,12524],{},"Future Development Plans",[6411,12526,12527,12530],{},[19,12528,12529],{},"Active",": Continuous enhancements and new features planned; updates tracked on GitHub project board",[6411,12532,12533,12536],{},[19,12534,12535],{},"Less transparent",": No detailed roadmap; future features are less clearly communicated",[31,12538,1425],{"id":1424},[15,12540,12541],{},"FlowFuse Dashboard is well-suited for users seeking a user-friendly, low-code solution with a wide range of pre-built elements and strong community support. Its ease of use and active development make it ideal for quick deployment and minimal technical overhead.",[15,12543,12544],{},"UI-Builder, however, offers nice performance and customization, benefiting users with coding expertise who need highly tailored UIs. Despite its flexibility and faster load times, it requires more complex setup and migration.",{"title":103,"searchDepth":104,"depth":104,"links":12546},[12547,12551,12555,12559,12563,12567,12571,12575,12579,12583,12587,12591,12595,12599,12603,12607,12611,12612],{"id":11813,"depth":107,"text":11814,"children":12548},[12549,12550],{"id":11817,"depth":294,"text":3465},{"id":11827,"depth":294,"text":11828},{"id":11838,"depth":107,"text":11839,"children":12552},[12553,12554],{"id":11842,"depth":294,"text":3465},{"id":11848,"depth":294,"text":11828},{"id":11854,"depth":107,"text":11855,"children":12556},[12557,12558],{"id":11870,"depth":294,"text":3465},{"id":11884,"depth":294,"text":11828},{"id":11893,"depth":107,"text":11894,"children":12560},[12561,12562],{"id":11897,"depth":294,"text":3465},{"id":11918,"depth":294,"text":11828},{"id":11939,"depth":107,"text":11940,"children":12564},[12565,12566],{"id":11943,"depth":294,"text":3465},{"id":11949,"depth":294,"text":11828},{"id":11955,"depth":107,"text":11956,"children":12568},[12569,12570],{"id":11959,"depth":294,"text":3465},{"id":11969,"depth":294,"text":11828},{"id":11975,"depth":107,"text":11976,"children":12572},[12573,12574],{"id":11979,"depth":294,"text":3465},{"id":11996,"depth":294,"text":11828},{"id":12002,"depth":107,"text":12003,"children":12576},[12577,12578],{"id":12006,"depth":294,"text":3465},{"id":12015,"depth":294,"text":11828},{"id":12021,"depth":107,"text":12022,"children":12580},[12581,12582],{"id":12025,"depth":294,"text":3465},{"id":12037,"depth":294,"text":11828},{"id":12043,"depth":107,"text":12044,"children":12584},[12585,12586],{"id":12047,"depth":294,"text":3465},{"id":12064,"depth":294,"text":11828},{"id":12070,"depth":107,"text":12071,"children":12588},[12589,12590],{"id":12074,"depth":294,"text":3465},{"id":12086,"depth":294,"text":12087},{"id":12093,"depth":107,"text":12094,"children":12592},[12593,12594],{"id":12097,"depth":294,"text":3465},{"id":12103,"depth":294,"text":11828},{"id":12109,"depth":107,"text":12110,"children":12596},[12597,12598],{"id":12113,"depth":294,"text":3465},{"id":12119,"depth":294,"text":11828},{"id":12125,"depth":107,"text":12126,"children":12600},[12601,12602],{"id":12129,"depth":294,"text":3465},{"id":12144,"depth":294,"text":11828},{"id":12159,"depth":107,"text":12160,"children":12604},[12605,12606],{"id":12163,"depth":294,"text":3465},{"id":12169,"depth":294,"text":11828},{"id":12175,"depth":107,"text":12176,"children":12608},[12609,12610],{"id":12179,"depth":294,"text":3465},{"id":12203,"depth":294,"text":11828},{"id":12209,"depth":107,"text":12210},{"id":1424,"depth":107,"text":1425},{"type":748,"title":12614,"description":12615},"Build and Deploy Node-RED Dashboards in Production","FlowFuse gives you everything you need to build, secure, and scale Node-RED Dashboard applications, with team collaboration, version control, and production-ready deployment built in.","2024-08-06","Compare FlowFuse Dashboard and UI-Builder. Discover their installation ease, customization, performance, and support to find the best solution for your needs.","\u002Fblog\u002F2024\u002F08\u002Fimages\u002Fdashboard-2-vs-ui-builder.png",{"keywords":12620,"excerpt":12621},"node red dashboard, node-red dashboard, node red ui builder, ui builder node red, flexdash, node red dashboard alternative, node red alternative dashboard, flowfuse dashboard",{"type":12,"value":12622},[12623],[15,12624,11810],{},"\u002Fblog\u002F2024\u002F08\u002Fcomparing-dashboard-2-with-uibuilder",{"title":11804,"description":12617},{"loc":12625},"blog\u002F2024\u002F08\u002Fcomparing-dashboard-2-with-uibuilder","Understanding the Differences Between FlowFuse Dashboard and UI-Builder",[3124,138,12631,12632],"dashboard","comparing node-red dashboards","FlowFuse Dashboard (Dashboard 2.0) and UI-Builder are the two most popular Node-RED dashboard solutions after the deprecation of Node-RED Dashboard 1.0. FlowFuse Dashboard offers an easier low-code setup, a guided migration path from Dashboard 1.0, and active development, while UI-Builder requires deeper coding knowledge and a full rebuild when migrating.","mMBXQDhItyfWRFoWDWJPQ1ZQZnPWSMia3yFFxb8wRMk",{"id":12636,"title":11340,"authors":12637,"body":12638,"cta":3,"date":12913,"description":12914,"extension":116,"image":12915,"lastUpdated":3,"meta":12916,"navigation":129,"path":12950,"seo":12951,"sitemap":12952,"stem":12953,"subtitle":12914,"tags":12954,"tldr":3,"video":3,"__hash__":12955},"blog\u002Fblog\u002F2024\u002F07\u002Fbuilding-on-flowfuse-devices.md",[8417],{"type":12,"value":12639,"toc":12903},[12640,12643,12646,12665,12671,12675,12678,12686,12689,12715,12718,12726,12730,12733,12742,12774,12777,12827,12831,12834,12840,12871,12875,12879,12886,12890,12893],[15,12641,12642],{},"{% from \"stories\u002Fcustomer-story.njk\" import storyTile %}",[15,12644,12645],{},"FlowFuse has established a rich ecosystem of products to help you build bespoke, powerful, low-code applications.",[15,12647,12648,12649,853,12654,1413,12659,12664],{},"We've seen customers utilizing these to ",[63,12650,12653],{"href":12651,"rel":12652},"https:\u002F\u002Fflowfuse.com\u002Fcustomer-stories\u002Fleveraging-node-red-and-flowfuse-to-automate-precision-manufacturing\u002F",[67],"revolutionise precision manufacturing",[63,12655,12658],{"href":12656,"rel":12657},"https:\u002F\u002Fflowfuse.com\u002Fcustomer-stories\u002Fnode-red-building-management\u002F",[67],"automate building management",[63,12660,12663],{"href":12661,"rel":12662},"https:\u002F\u002Fflowfuse.com\u002Fcustomer-stories\u002Fun-wmo-nr-data-sharing\u002F",[67],"modernize the distribution of global weather data",", just to name a few examples.",[15,12666,12667,12668,29],{},"In this series of articles, we'll be taking a look at the common architectures and design patterns we are seeing used across our customer base, and how you can use these to build your own applications. To kick things off, this article will focus on ",[19,12669,12670],{},"\"Remote Device Monitoring\"",[31,12672,12674],{"id":12673},"use-case","Use Case",[15,12676,12677],{},"You have hundreds, if not, thousands of devices deployed remotely. These could be anything from sensors, to actuators, to entire machines. You need to monitor these devices, and potentially control them. It's important to know when they are online, that they're running optimally and if something is about to, or already has, gone wrong.",[15,12679,12680,12684],{},[165,12681],{"alt":12682,"dataZoomable":103,"src":12683},"Diagram showing the breakdown of each component that makes up a \"Device Monitoring\" use case.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fdevice-monitoring-use-case.png",[170,12685,12682],{},[15,12687,12688],{},"Breaking it down we have the following fundamental requirements:",[39,12690,12691,12697,12703,12709],{},[42,12692,12693,12696],{},[19,12694,12695],{},"Read:"," Reading data from the devices using the relevant protocol",[42,12698,12699,12702],{},[19,12700,12701],{},"Parse:"," Process the data into a computational format",[42,12704,12705,12708],{},[19,12706,12707],{},"Compute\u002FMonitor:"," Analysis & monitoring of the data from all devices",[42,12710,12711,12714],{},[19,12712,12713],{},"Action:"," Taking action based on the data from the devices",[15,12716,12717],{},"Then, we have to consider that this functionality needs to be deployed out to thousands of devices. So, our final requirement is:",[39,12719,12720],{},[42,12721,12722,12725],{},[19,12723,12724],{},"Maintain:"," If we update functionality on one device, we may need to be able to easily roll those updates out to other devices. We need to sure it's easy to update and deploy to our devices, and if anything goes wrong, we should be able to easily find out what the problem is, and get easy access to fix it.",[31,12727,12729],{"id":12728},"architecture","Architecture",[15,12731,12732],{},"Let's consider an example architecture set in an automotive plant:",[15,12734,12735,12739],{},[165,12736],{"alt":12737,"dataZoomable":103,"src":12738},"Diagram showing the architecture of a \"Device Monitoring\" use case in an automotive plant.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fdevice-monitoring-architecture.png",[170,12740,12741],{},"Diagram showing an example architecture of a \"Device Monitoring\" use case in an automotive plant.",[39,12743,12744,12750,12756,12762,12768],{},[42,12745,12746,12749],{},[19,12747,12748],{},"Laser Welding Machine:"," Here we have a small system built around a single piece of laser welding hardware. The hardware is connected to a PLC via local network, and the PLC is connected to a local server. Each component here can have Node-RED installed, managed by FlowFuse and accessible via the Device Agent. Node-RED would enable extraction of data from the hardware, and provide a local, bespoke, Dashboard on a nearby PC for monitoring the hardware.",[42,12751,12752,12755],{},[19,12753,12754],{},"Body Shop:"," Here we have several piece of machinery, each with the \"FlowFuse Device Agent\" installed. This allows us to manage the Node-RED deployments on these machines remotely from FlowFuse, and easily extract data from the machines for analysis.",[42,12757,12758,12761],{},[19,12759,12760],{},"Plant:"," We have multiple servers running at the Planet-level, generally one for each \"Shop\", each with their own Device Agent installed, again for easy remote management and deployment of Node-RED, e.g. Dashboards that provide a single HMI for monitoring hardware across a full shop. Here, we also have our instance of FlowFuse. This is the central point for managing all of our Node-RED deployments across the factory floor.",[42,12763,12764,12767],{},[19,12765,12766],{},"Company IT Dept:"," The general IT department of the company would provide multiple servers and services, accessible to a range of departments and the plant. Node-RED could act as a bridge between the Plant and the Company IT Dept, allowing us to easily extract data from the Plant and send it to the Company IT Dept, and vice-versa.",[42,12769,12770,12773],{},[19,12771,12772],{},"Cloud:"," Here we demonstrate how a company may have external Cloud-based services they're dependent upon. For example, image analytics services. Node-RED can be used to parse machine imagery, and integrate straight with these external services from the FlowFuse server.",[15,12775,12776],{},"This diagram also demonstrates a sample of the rich ecosystem of communication protocols that Node-RED can support:",[39,12778,12779,12785,12791,12797,12803,12809,12815,12821],{},[42,12780,12781,12784],{},[19,12782,12783],{},"TCP:"," Read and process data from from bespoke and custom devices",[42,12786,12787,12790],{},[19,12788,12789],{},"EtherNet\u002FIP:"," Collect data from Allen Bradley and other EtherNet\u002FIP devices",[42,12792,12793,12796],{},[19,12794,12795],{},"MQTT:","  Modern devices and factory generated IIoT data",[42,12798,12799,12802],{},[19,12800,12801],{},"OPC-UA:","  Communicate with newer PLCs and OPC Servers",[42,12804,12805,12808],{},[19,12806,12807],{},"Modbus:","  Data collection from existing Modbus enabled devices like Temperature Probes, Invertors, Encoders",[42,12810,12811,12814],{},[19,12812,12813],{},"MC Protocol:","  Gather data from Mitsubishi PLCs",[42,12816,12817,12820],{},[19,12818,12819],{},"FINS:","  Gather data from OMRON PLCs",[42,12822,12823,12826],{},[19,12824,12825],{},"Siemens S7:","   Gather data from Siemens PLCs using the S7 Protocol",[399,12828,12830],{"id":12829},"flowfuse-technology-stack","FlowFuse Technology Stack",[15,12832,12833],{},"Given the above architecture, let's take a look at the relevant FlowFuse offerings and see what they're contributing:",[15,12835,12836],{},[165,12837],{"alt":12838,"dataZoomable":103,"src":12839},"Lineup of each of the FlowFuse offerings","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fff-ecosystem-lineup.png",[39,12841,12842,12847,12853,12859,12865],{},[42,12843,12844,12846],{},[19,12845,3166],{}," Low-code, drag-and-drop integration platform. Here we'd be using it in multiple places to read and parse data from the hardware, define the application logic, conduct analysis and provide alerting.",[42,12848,12849,12852],{},[19,12850,12851],{},"FlowFuse Dashboard:"," An add-on to Node-RED for building interactive user interfaces and dashboards. We use this here to provide visual feedback on local Dashboards for some devices, as well as at the Planet-level to provide an at-a-glance view of our plant.",[42,12854,12855,12858],{},[19,12856,12857],{},"FlowFuse:"," Centralized platform that provides a single entry point to manage all of your Node-RED applications and deployments, from your device inetrgators, through to your dashboards. FlowFuse provides role-based access control out of the box, so we can easily control who has access to flows, Dashboards and other configurations.",[42,12860,12861,12864],{},[19,12862,12863],{},"FlowFuse Device Agent:"," Installed onto the relevant servers and hardware, this links you directly, and securely to FlowFuse. Here, we're deploying it to multiple pieces of machinery, as well as local servers, such that we can easily manage (deploy, upgrade, debug) all of those Node-RED deployments remotely from FlowFuse.",[42,12866,12867,12870],{},[19,12868,12869],{},"FlowFuse Project Nodes:"," A small collection of nodes for Node-RED, running on FlowFuse, that provide communication over a secure (MQTT-based) connection between devices (our Node-RED deployments on our hardware in this case) and our hosted instances of Node-RED on FlowFuse. Extremely useful for reporting live sensor data back to FlowFuse for analysis, and for reporting information back to our devices.",[31,12872,12874],{"id":12873},"what-next","What Next?",[399,12876,12878],{"id":12877},"deploy-to-flowfuse","Deploy to FlowFuse",[15,12880,12881,12882,29],{},"If you're interested in discussing how your company could benefit from this design pattern, please do ",[63,12883,12885],{"href":12884},"\u002Fcontact-us\u002F?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20contact%20us&utm_term=high_intent&utm_content=Building%20on%20FlowFuse%3A%20Remote%20Device%20Monitoring","get in touch",[399,12887,12889],{"id":12888},"customer-stories","Customer Stories",[15,12891,12892],{},"If you want to deep dive further into how this design pattern has been used by our customers, we have some customer stories that you might find interesting:",[39,12894,12902],{"className":12895},[12896,12897,12898,12899,12900,12901],"grid","grid-cols-1","sm:grid-cols-2","gap-4","px-0","list-none","\n    {%- for story in stories | sort(attribute='item.date') | reverse -%}\n    {{ storyTile(title=story.title, url=story.url, brand=story.brand, logo=story.logo, image=story.image) }}\n    {%- endfor -%}\n",{"title":103,"searchDepth":104,"depth":104,"links":12904},[12905,12906,12909],{"id":12673,"depth":107,"text":12674},{"id":12728,"depth":107,"text":12729,"children":12907},[12908],{"id":12829,"depth":294,"text":12830},{"id":12873,"depth":107,"text":12874,"children":12910},[12911,12912],{"id":12877,"depth":294,"text":12878},{"id":12888,"depth":294,"text":12889},"2024-07-17","In this article we take a look at how elements of the FlowFuse ecosystem can be used to build powerful IoT applications for monitoring remote devices.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Ftile-building-on-flowfuse-devices.png",{"templateEngineOverride":12917,"stories":12918,"excerpt":12929},"njk, md",[12919,12924],{"title":12920,"logo":103,"image":12921,"brand":12922,"url":12923},"Leveraging Node-RED and FlowFuse to Revolutionize Irrigation","\u002Fimages\u002Fstories\u002Fpidd-view.png","Paloma Irrigation and Drainage District","\u002Fcustomer-stories\u002Fleveraging-node-red-and-flowfuse-to-revolutionize-irrigation",{"title":12925,"logo":103,"image":12926,"brand":12927,"url":12928},"Revolutionizing Precision Manufacturing with Node-RED","\u002Fimages\u002Fstories\u002Fabrasive_tech.jpg","Abrasive Technology","\u002Fcustomer-stories\u002Fleveraging-node-red-and-flowfuse-to-automate-precision-manufacturing",{"type":12,"value":12930},[12931,12933,12935,12946],[15,12932,12642],{},[15,12934,12645],{},[15,12936,12648,12937,853,12940,1413,12943,12664],{},[63,12938,12653],{"href":12651,"rel":12939},[67],[63,12941,12658],{"href":12656,"rel":12942},[67],[63,12944,12663],{"href":12661,"rel":12945},[67],[15,12947,12667,12948,29],{},[19,12949,12670],{},"\u002Fblog\u002F2024\u002F07\u002Fbuilding-on-flowfuse-devices",{"title":11340,"description":12914},{"loc":12950},"blog\u002F2024\u002F07\u002Fbuilding-on-flowfuse-devices",[8650,136,138,10],"0wc6ovqOXQvECZEGwDoEAcvjcYiJycoff8e--WIqhdE",{"id":12957,"title":12958,"authors":12959,"body":12960,"cta":13356,"date":12913,"description":13359,"extension":116,"image":13360,"lastUpdated":3,"meta":13361,"navigation":129,"path":13369,"seo":13370,"sitemap":13371,"stem":13372,"subtitle":13373,"tags":13374,"tldr":3,"video":3,"__hash__":13377},"blog\u002Fblog\u002F2024\u002F07\u002Fhow-to-setup-sso-saml-for-the-node-red.md","How to Set Up SSO SAML for Node-RED",[144],{"type":12,"value":12961,"toc":13344},[12962,12969,12973,12976,12980,12983,12990,13034,13038,13064,13068,13087,13099,13103,13111,13118,13123,13130,13135,13142,13147,13154,13159,13166,13187,13191,13196,13203,13211,13218,13226,13233,13238,13245,13253,13260,13272,13276,13279,13284,13291,13296,13303,13311,13315,13323,13334,13339,13341],[15,12963,12964,12965,29],{},"SSO plays a crucial role in modern enterprise environments by simplifying user authentication across multiple applications. At FlowFuse, we recognize the significance of SSO and offer robust support for integrating it with your self-hosted platform. In this comprehensive guide, we will focus on configuring SSO SAML, specifically using Google as the Identity Provider (IdP). We also support SSO with LDAP. For more information, refer to ",[63,12966,12968],{"href":12967},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fldap\u002F","Setting up LDAP SSO for your Self-Hosted FlowFuse",[31,12970,12972],{"id":12971},"what-is-sso","What is SSO?",[15,12974,12975],{},"Single Sign-On (SSO) is a technology that allows users to access multiple applications or services with one set of login credentials. When a user logs in to one central system, known as the Identity Provider (IdP) such as Google, Microsoft Azure AD, or Okta, it authenticates the user and creates a session token. This token is then used to access other connected applications without requiring the user to log in again. The IdP verifies the token and shares the authentication information with other applications, ensuring secure and seamless access across multiple platforms. This reduces the need to remember multiple passwords and enhances security by centralizing authentication.",[31,12977,12979],{"id":12978},"understanding-saml","Understanding SAML",[15,12981,12982],{},"SAML (Security Assertion Markup Language) is a protocol used in SSO scenarios to exchange authentication and authorization data between an Identity Provider (IdP) and a Service Provider (SP). Here’s how it works step-by-step:",[15,12984,12985],{},[165,12986],{"alt":12987,"src":12988,"title":12989,"dataZoomable":103},"\"Image showing the working process of SSO SAML\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002FSAML-SSO.png","Image showing the working process of SSO SAML",[271,12991,12992,12998,13004,13010,13016,13022,13028],{},[42,12993,12994,12997],{},[19,12995,12996],{},"User Request",": A user attempts to access a service or application that is protected by SAML-based SSO.",[42,12999,13000,13003],{},[19,13001,13002],{},"Redirect to IdP",": The Service Provider (SP), recognizing that the user needs authentication, redirects them to the Identity Provider (IdP).",[42,13005,13006,13009],{},[19,13007,13008],{},"User Authentication",": The IdP prompts the user to authenticate themselves. This authentication can involve various methods, such as username\u002Fpassword, multi-factor authentication (MFA), or other authentication mechanisms supported by the IdP.",[42,13011,13012,13015],{},[19,13013,13014],{},"Generating SAML Assertion",": Upon successful authentication, the IdP generates a SAML assertion. This assertion contains information about the user's identity (like username, email) and any attributes or roles associated with the user.",[42,13017,13018,13021],{},[19,13019,13020],{},"Sending SAML Assertion",": The IdP sends this SAML assertion back to the Service Provider (SP), typically through the user's browser. The assertion is digitally signed by the IdP to ensure its authenticity and integrity.",[42,13023,13024,13027],{},[19,13025,13026],{},"Assertion Validation",": The Service Provider (SP) receives the SAML assertion. It validates the assertion to ensure it comes from a trusted IdP and that the assertions' contents are accurate and have not been tampered with.",[42,13029,13030,13033],{},[19,13031,13032],{},"Granting Access",": If the assertion is valid, the SP grants access to the user based on the information in the SAML assertion. This allows the user to access the protected application or service without needing to provide separate credentials to the SP.",[31,13035,13037],{"id":13036},"benefits-of-using-sso-in-the-workspace","Benefits of Using SSO in the Workspace",[271,13039,13040,13046,13052,13058],{},[42,13041,13042,13045],{},[19,13043,13044],{},"Improved User Experience",": Users only need to remember one set of credentials, making the login process simpler and reducing password fatigue. For instance, In a manufacturing environment, employees often need to access multiple systems, such as Node-RED, ERP systems, and quality control applications. With SSO, they only need to remember one set of credentials, simplifying the login process and reducing password fatigue.",[42,13047,13048,13051],{},[19,13049,13050],{},"Simplified Management",": Administrators can manage user access and permissions from a single platform, making it easier to onboard new employees and revoke access when someone leaves. This centralized management ensures that only authorized personnel can access the application flows.",[42,13053,13054,13057],{},[19,13055,13056],{},"Increased Security",": Centralized authentication via SSO reduces the risk of weak or reused passwords, allowing for the implementation of stronger security policies. This is critical in a workspace where sensitive data and operational systems must be protected against unauthorized access.",[42,13059,13060,13063],{},[19,13061,13062],{},"Enhanced Productivity",": SSO allows employees to log in once and gain access to all necessary applications, saving time and reducing interruptions.",[31,13065,13067],{"id":13066},"setting-up-sso-for-your-self-hosted-flowfuse-platform","Setting up SSO For Your Self-Hosted FlowFuse Platform",[15,13069,13070,13071,1413,13076,13081,13082,13086],{},"Before proceeding, ensure you have deployed FlowFuse on your server with an Enterprise license and configured it with email. Without email configuration, the SSO setup option won't appear in your FlowFuse.Furthermore, Make sure you have a ",[63,13072,13075],{"href":13073,"rel":13074},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=Rc7BT7PDqFs",[67],"Google Workspace account created",[63,13077,13080],{"href":13078,"rel":13079},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=JIOaLKsz2R0",[67],"verified your domain"," within Google Workspace. FlowFuse supports other providers for SAML SSO as well; check our ",[63,13083,13085],{"href":13084},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fsaml\u002F#providers","documentation on supported providers"," for details.",[15,13088,13089,13090,13094,13095,29],{},"If you haven't deployed FlowFuse yet, refer to our ",[63,13091,13093],{"href":13092},"\u002Fdocs\u002Finstall\u002Fintroduction\u002F","documentation on installing FlowFuse"," or our blog on ",[63,13096,13098],{"href":13097},"\u002Fblog\u002F2024\u002F07\u002Fdeploying-flowfuse-with-docker\u002F","deploying FlowFuse on Ubuntu with Docker",[399,13100,13102],{"id":13101},"configuring-sso-in-your-self-hosted-flowfuse","Configuring SSO in your Self-Hosted FlowFuse",[271,13104,13105,13108],{},[42,13106,13107],{},"To configure FlowFuse with SSO, make sure you are logged in as an administrator.",[42,13109,13110],{},"Go to Admin settings by clicking on the profile icon in the top-right corner and then selecting \"Admin settings\".",[15,13112,13113],{},[165,13114],{"alt":13115,"src":13116,"title":13117},"\"Screenshot showing the admin settings option in the profile icon\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fadmin-setting-option.png","Screenshot showing the admin settings option in the profile icon",[271,13119,13120],{"start":294},[42,13121,13122],{},"Click on \"Settings\" from the left sidebar and switch to the SSO section.",[15,13124,13125],{},[165,13126],{"alt":13127,"src":13128,"title":13129,"dataZoomable":103},"\"Screenshot of platform showing the 'admin settings' 'SSO' tab option\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fsettings-sso-setting-section.png","Screenshot of platform showing the 'admin settings' 'SSO' tab option",[271,13131,13132],{"start":104},[42,13133,13134],{},"Click on the top-right \"Create SSO configuration\".",[15,13136,13137],{},[165,13138],{"alt":13139,"src":13140,"title":13141,"dataZoomable":103},"\"Screenshot of platform showing the 'Creating configuration' button\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcreate-sso-config-button.png","Screenshot of platform showing the 'Creating configuration' button",[271,13143,13144],{"start":1868},[42,13145,13146],{},"Enter a name for your configuration, then enter the domain that the platform will use for SSO and select \"SAML\" option and click on \"create configuration\" button.",[15,13148,13149],{},[165,13150],{"alt":13151,"src":13152,"title":13153,"dataZoomable":103},"\"Screenshot of platform form for creating sso configuration\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fsso-config-initial-form.png","Screenshot of platform form for creating sso configuration",[271,13155,13156],{"start":1868},[42,13157,13158],{},"In the next tab, you'll find the ACS URL and Entity ID. Copy and save them somewhere in the notepad.",[15,13160,13161],{},[165,13162],{"alt":13163,"src":13164,"title":13165,"dataZoomable":103},"\"Screenshot of advance sso form showing the ACS URL and Entity ID and other feilds for configuration\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fsso-config-next-form.png","Screenshot of advance sso form showing the ACS URL and Entity ID and other feilds for configuration",[271,13167,13168,13184],{"start":1896},[42,13169,13170,13171],{},"Tick \"Manage roles using group assertions\" if needed. If enabled, keep the Group Assertion Name to its default value and select the team scope:\n",[39,13172,13173,13181],{},[42,13174,13175,13176,13180],{},"\"Apply to selected teams\" restricts management to the provided list of groups, For adding the list refer to ",[63,13177,13179],{"href":13178},"\u002Fdocs\u002Fadmin\u002Fsso\u002Fsaml\u002F#saml-groups-configuration","SAML Groups configuration",", suitable for shared-tenancy platforms like FlowFuse Cloud.",[42,13182,13183],{},"\"Apply to all teams\" allows SAML groups to manage all teams, suitable for a self-hosted installation with a single SSO configuration.",[42,13185,13186],{},"Click on \"Update configuration\", we will back to update this configuration with our configuration provided by our Google workpsace later.",[399,13188,13190],{"id":13189},"creating-the-saml-app-in-google-workspace","Creating the SAML APP in Google Workspace.",[271,13192,13193],{},[42,13194,13195],{},"Open the main menu from the top-right corner by clicking on menu icon.",[15,13197,13198],{},[165,13199],{"alt":13200,"src":13201,"title":13202,"dataZoomable":103},"\"Screenshot showing the 'web and mobile apps' option in the sidebar in the Google workspace admin console\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fmain-menus-web-and-mobile-apps-option.png","Screenshot showing the 'web and mobile apps' option in the sidebar in the Google workspace admin console",[271,13204,13205,13208],{"start":107},[42,13206,13207],{},"Click on the \"Apps\" icon, which expands additional choices. Then click on the \"Web and mobile apps.\"",[42,13209,13210],{},"Then the new tab opens, click on to the \"Add app\" from the top.",[15,13212,13213],{},[165,13214],{"alt":13215,"src":13216,"title":13217,"dataZoomable":103},"\"Screenshot showing the 'Add app' and 'Add custom SAML apps' option in the Google workspace admin console\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fadd-custom-saml-apps-option.png","Screenshot showing the 'Add custom SAML apps' option in the Google workspace admin console",[271,13219,13220,13223],{"start":104},[42,13221,13222],{},"The options open select the \"Add custom SAML apps.\"",[42,13224,13225],{},"Enter the app name description and upload an icon for the app.",[15,13227,13228],{},[165,13229],{"alt":13230,"src":13231,"title":13232,"dataZoomable":103},"\"Screenshot showing the form asking for app information for we are going to create the SAML app configuration in the Google workspace admin console\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fform-to-add-app-info.png","Screenshot showing the form asking for app information for we are going to create the SAML app configuration in the Google workspace admin console",[271,13234,13235],{"start":1896},[42,13236,13237],{},"Now copy your Copy the SSO URL, entity ID and certificate or you can simply download it in the one file by clicking on the Download metadata button.",[15,13239,13240],{},[165,13241],{"alt":13242,"src":13243,"title":13244,"dataZoomable":103},"\"Screenshot configuration provided the Google workspace admin console for SSO SAML\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fconfig-details-for-sso.png","Screenshot configuration provided the Google workspace admin console for SSO SAML",[271,13246,13247,13250],{"start":1623},[42,13248,13249],{},"Click on the continue.",[42,13251,13252],{},"In the next tab, enter the ACS URL and entity id provided by your self-hosted flowfuse platform.",[15,13254,13255],{},[165,13256],{"alt":13257,"src":13258,"title":13259,"dataZoomable":103},"\"Screenshot form asking for the configuration detailes provided by your self-hosted FlowFuse\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fform-to-add-the-config-provided-flowfuse.png","Screenshot form asking for the configuration detailes provided by your self-hosted FlowFuse",[271,13261,13262,13269],{"start":1930},[42,13263,13264,13265,13268],{},"Enter the start URL as ",[924,13266,13267],{},"forge.\u003Cyour-domain>.com",", we are specify that after successfull sign in the user should be redirected to this url.",[42,13270,13271],{},"Next, click on \"Continue\" button and then click on \"finish\".",[399,13273,13275],{"id":13274},"updating-the-flowfuse-sso-configuration-and-enabling-it","Updating the FlowFuse SSO configuration and enabling it",[15,13277,13278],{},"Now that we have created the SAML app in the workspace, we need to update the FlowFuse configuration and connect it to the app created in the workspace to enable SSO.",[271,13280,13281],{},[42,13282,13283],{},"Go to the SSO section of the admin settings in your self-hosted FlowFuse platform. Click on the three-dot icon located at the right corner of the added configuration and select \"Edit.\"",[15,13285,13286],{},[165,13287],{"alt":13288,"src":13289,"title":13290,"dataZoomable":103},"\"Screenshot showing three dot icons in your self-hosted FlowFuse\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fedit-sso-config.png","Screenshot showing three dot icons in your self-hosted FlowFuse",[271,13292,13293],{"start":107},[42,13294,13295],{},"Add the copied values for the fields: Identity Provider Single Sign-On URL, Identity Provider Issuer ID \u002F URL, and X.509 Certificate Public Key provided by the provider. I instructed you in the above section to copy or download them.",[15,13297,13298],{},[165,13299],{"alt":13300,"src":13301,"title":13302,"dataZoomable":103},"\"Screenshot showing the form updated with configuration provided by Google workspace\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fsso-config-form.png","Screenshot showing the form updated with configuration provided by Google workspace",[271,13304,13305,13308],{"start":294},[42,13306,13307],{},"Click on the \"Active\" option to enable the SSO in the same form.",[42,13309,13310],{},"Click on \"Update configuration\".",[399,13312,13314],{"id":13313},"signing-in-using-sso","Signing in Using SSO",[15,13316,13317,13318,13322],{},"To sign in using SSO, users of your self-hosted FlowFuse must have a FlowFuse account created with an email ID associated with the domain configured with SSO. For more information, refer to ",[63,13319,13321],{"href":13320},"\u002Fdocs\u002Fadmin\u002Fuser_management\u002F#creating-new-users","creating users in FlowFuse",". Additionally, the user must already be logged in with that email in the browser.",[271,13324,13325,13328,13331],{},[42,13326,13327],{},"Open your platform in the browser, Enter the email address in the username\u002Femail field.",[42,13329,13330],{},"Click on \"Login\".",[42,13332,13333],{},"A Google tab will open, displaying the email addresses you are signed in with. Select the email address you entered into the username\u002Femail field.",[15,13335,13336],{},[170,13337,13338],{},"Note: Admin users will still be able to log in with their original FlowFuse username\u002Fpassword - this ensures they don't get locked out of the platform if there is a problem with the SSO configuration",[31,13340,1425],{"id":1424},[15,13342,13343],{},"In this guide, we've covered setting up SSO SAML for your self-hosted FlowFuse platform, exploring how SSO and SAML enhance user experience, improve security, and simplify management. You've learned to create an SSO configuration in FlowFuse, set up a SAML app in Google Workspace, and enable seamless authentication.",{"title":103,"searchDepth":104,"depth":104,"links":13345},[13346,13347,13348,13349,13355],{"id":12971,"depth":107,"text":12972},{"id":12978,"depth":107,"text":12979},{"id":13036,"depth":107,"text":13037},{"id":13066,"depth":107,"text":13067,"children":13350},[13351,13352,13353,13354],{"id":13101,"depth":294,"text":13102},{"id":13189,"depth":294,"text":13190},{"id":13274,"depth":294,"text":13275},{"id":13313,"depth":294,"text":13314},{"id":1424,"depth":107,"text":1425},{"type":8471,"title":13357,"description":13358},"Enterprise-Grade Security for Your Node-RED Platform","SSO with SAML and LDAP, RBAC, audit logging, and SOC 2 compliance, all included in FlowFuse Enterprise for teams that need centralized authentication and production-grade governance.","Learn how to configure SSO SAML for your self-hosted FlowFuse platform using Google as the Identity Provider (IdP). Simplify user authentication across applications with this step-by-step guide.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fsso-saml-with-node-red.png",{"keywords":13362,"excerpt":13363},"node red security, node red security, sso with node red nodered security, secure node red, securing node red",{"type":12,"value":13364},[13365],[15,13366,12964,13367,29],{},[63,13368,12968],{"href":12967},"\u002Fblog\u002F2024\u002F07\u002Fhow-to-setup-sso-saml-for-the-node-red",{"title":12958,"description":13359},{"loc":13369},"blog\u002F2024\u002F07\u002Fhow-to-setup-sso-saml-for-the-node-red","Step-by-step guide on setting up SSO SAML for your self-hosted FlowFuse platform",[3124,138,13375,13376],"node-red security","sso","RkjmV2xcVgpLoAeFZUo-iLZJEuQ5-e-1W7Q2JGr9Cjg",{"id":13379,"title":13380,"authors":13381,"body":13382,"cta":13750,"date":13753,"description":13754,"extension":116,"image":13755,"lastUpdated":6203,"meta":13756,"navigation":129,"path":13762,"seo":13763,"sitemap":13764,"stem":13765,"subtitle":13766,"tags":13767,"tldr":13769,"video":3,"__hash__":13770},"blog\u002Fblog\u002F2024\u002F07\u002Fcalling-python-script-from-node-red.md","Calling a Python script from Node-RED (2026)",[144],{"type":12,"value":13383,"toc":13742},[13384,13387,13391,13394,13397,13401,13410,13413,13428,13435,13438,13450,13461,13465,13468,13485,13493,13505,13512,13520,13543,13550,13554,13557,13565,13585,13588,13595,13604,13641,13645,13648,13677,13699,13714,13734,13736,13739],[15,13385,13386],{},"Python's robust data processing capabilities and extensive libraries are well-known in programming. When combined with Node-RED, these technologies can synergize to elevate data analytics and automation. This guide walks you through integrating Python scripts with Node-RED. You'll gain practical insights, troubleshooting tips, and effective techniques for executing scripts, enabling you to leverage this powerful combination for your IoT projects.",[31,13388,13390],{"id":13389},"why-use-python-with-node-red","Why use python with Node-RED",[15,13392,13393],{},"Integrating Python and Node-RED can significantly enhance your IoT and automation initiatives by leveraging their distinct strengths. Node-RED excels in creating easy workflows, efficiently processing data streams, and integrating hardware, APIs, and. Meanwhile, Python offers a rich set of libraries for advanced tasks such as machine learning and AI, pivotal in realizing Industry 4.0 concepts.",[15,13395,13396],{},"This combination allows developers to build robust and flexible solutions. For instance, while Node-RED manages data flow and device communication, Python can perform complex analytics, and predictive modeling, or integrate with AI frameworks. This integration bridges the gap between data collection and actionable insights, enabling systems to make informed decisions autonomously.",[31,13398,13400],{"id":13399},"installing-python","Installing Python",[15,13402,13403,13404,13409],{},"When executing Python scripts, it's essential to have the Python runtime installed on your system. Before proceeding, make sure you have it installed. You can follow the ",[63,13405,13408],{"href":13406,"rel":13407},"https:\u002F\u002Fwiki.python.org\u002Fmoin\u002FBeginnersGuide\u002FDownload",[67],"official guide"," for instructions.",[15,13411,13412],{},"To verify if Python is installed, open your terminal and execute:",[1756,13414,13418],{"className":13415,"code":13416,"language":13417,"meta":103,"style":103},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","python --version\n","bash",[924,13419,13420],{"__ignoreMap":103},[1107,13421,13422,13425],{"class":1765,"line":1766},[1107,13423,13424],{"class":10543},"python",[1107,13426,13427],{"class":1966}," --version\n",[15,13429,13430],{},[165,13431],{"alt":13432,"dataZoomable":103,"src":13433,"title":13434},"\"Screenshot of terminal showing the python version installed, conforming it is installed\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcalling-python-script-from-node-red-py-conformation.png","Screenshot of terminal showing the python version installed, conforming it is installed",[15,13436,13437],{},"The above command displays the version of Python installed on your system as shown in the above image. If the above command doesn't work, try:",[1756,13439,13441],{"className":13415,"code":13440,"language":13417,"meta":103,"style":103},"python3 --version\n",[924,13442,13443],{"__ignoreMap":103},[1107,13444,13445,13448],{"class":1765,"line":1766},[1107,13446,13447],{"class":10543},"python3",[1107,13449,13427],{"class":1966},[15,13451,13452,13453,13456,13457,13460],{},"The specific command to use depends on how Python was installed and configured on your system. However, make sure to use ",[924,13454,13455],{},"python \u003Cfilename>.py"," if the first command works, or ",[924,13458,13459],{},"python3 \u003Cfilename>.py"," if the second command works, while executing Python scripts.",[31,13462,13464],{"id":13463},"executing-python-script-from-node-red","Executing Python Script from Node-RED",[15,13466,13467],{},"Let's now see how to call a Python script from Node-RED. First, we'll create a basic script file that contains a function to print text in the console based on input. Currently, the function uses hardcoded input. To create this file using Node-RED, import the following flow, deploy it, and press the inject button:",[15,13469,4917,13470,1150],{},[1107,13471,13472,13473,13475,13476,13478,13479,13481,13482,13484],{},"{\"id\":\"b9d7d6aff0016631\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,13474,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":240,\"y\":100,\"wires\":[[\"2e1daccf2a7b3d0f\"]]},{\"id\":\"d2d1450deaa588f4\",\"type\":\"file\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"filename\":\".\\example.py\",\"filenameType\":\"str\",\"appendNewline\":true,\"createDir\":false,\"overwriteFile\":\"true\",\"encoding\":\"none\",\"x\":570,\"y\":100,\"wires\":[[\"e140a8508fb10d96\"]]},{\"id\":\"e140a8508fb10d96\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":100,\"wires\":",[1107,13477],{},"},{\"id\":\"2e1daccf2a7b3d0f\",\"type\":\"template\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"field\":\"payload\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"def main():\\n    # Hardcoded value\\n    user_input = 20  \\n    \\n    # Check if the input is numeric\\n    if isinstance(user_input, int) or (isinstance(user_input, str) and user_input.isdigit()):\\n        number = int(user_input) if isinstance(user_input, str) else user_input\\n        \\n        # Conditionally render based on the input value\\n        if number \u003C 0:\\n            print(\"Negative number entered\")\\n        elif number == 0:\\n            print(\"Zero entered\")\\n        else:\\n            print(\"Positive number entered\")\\n    else:\\n        print(\"Invalid input. Please enter a valid number.\")\\n\\nif ",[19,13480,1914],{}," == \"",[19,13483,1797],{},"\":\\n    main()\\n\",\"output\":\"str\",\"x\":400,\"y\":100,\"wires\":[[\"d2d1450deaa588f4\"]]}",[15,13486,13487,13488,13492],{},"Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's ",[63,13489,13491],{"href":13490},"\u002Fnode-red\u002Fcore-nodes\u002Fexec\u002F","Exec"," node, which allows running commands on your system.",[271,13494,13495,13498],{},[42,13496,13497],{},"Drag an Inject node onto the canvas.",[42,13499,13500,13501,13504],{},"Drag an Exec node onto the canvas and Configure the command to ",[924,13502,13503],{},"python .\u002Fexample.py -u",". The -u flag prevents potential output buffering issues when executing Python scripts via exec.",[15,13506,13507],{},[165,13508],{"alt":13509,"dataZoomable":103,"src":13510,"title":13511},"\"Screenshot of the Exec node executing python file\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcalling-python-scrpt-from-node-red-exec-node.png","Screenshot of the Exec node executing python file",[271,13513,13514,13517],{"start":294},[42,13515,13516],{},"Drag a Debug node onto the canvas.",[42,13518,13519],{},"Connect the output of the Inject node to the input of the Exec node, and the output of the Exec node to the input of Debug node.",[15,13521,4917,13522,1150],{},[1107,13523,13524,13525,1955,13528,13531,13532,13534,13535,13537,13538,13540,13541,4957],{},"{\"id\":\"2e26b84c0ce17312\",\"type\":\"exec\",\"z\":\"FFF0000000000001\",\"command\":\"python .\u002Fexample.py -u\",\"addpay\":\"\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":460,\"y\":300,\"wires\":[[\"89589c56117004e0\"],",[1107,13526,13527],{},"\"7fdb901b144749c2\"",[1107,13529,13530],{},"\"3f49b49308941782\"","]},{\"id\":\"739e08c1ec77c2a1\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,13533,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":240,\"y\":300,\"wires\":[[\"2e26b84c0ce17312\"]]},{\"id\":\"89589c56117004e0\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Output\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":690,\"y\":260,\"wires\":",[1107,13536],{},"},{\"id\":\"7fdb901b144749c2\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Error\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":690,\"y\":300,\"wires\":",[1107,13539],{},"},{\"id\":\"3f49b49308941782\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Return code\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":710,\"y\":340,\"wires\":",[1107,13542],{},[15,13544,13545,13546,13549],{},"Now, when you deploy this flow and click on the inject node to execute the file, you should see the text 'Positive number entered' and ",[924,13547,13548],{},"{ code: 0 }",", which indicates your script has been successfully executed.",[31,13551,13553],{"id":13552},"reading-temperature-sensor-using-python-script","Reading Temperature Sensor using Python script",[15,13555,13556],{},"Having explored how to run a Python script within Node-RED with the basic practical example, let's move to a real-world scenario. We'll demonstrate how to read sensor data using Python, despite Node-RED providing numerous community-built nodes for this purpose. This approach provides deeper insights into integrating external scripts, showcasing the flexibility of Node-RED for custom solutions.",[15,13558,13559,13560,13564],{},"Before proceeding, ensure that Node-RED is running on a device connected to a temperature sensor. For detailed instructions, refer to ",[63,13561,13563],{"href":13562},"\u002Fnode-red\u002Fhardware\u002F","Setting Up Node-RED on Different Hardware",", In this case, we are running Node-RED on a Raspberry Pi 5 with a DHT11 sensor connected to it.",[271,13566,13567,13570,13576,13579,13582],{},[42,13568,13569],{},"Drag an Inject node onto the canvas, and set repeat to 1 seconds of interval.",[42,13571,13572,13573,13575],{},"Drag an Exec node and set the path to ",[924,13574,13455],{},", replace the filename with the name of the file which reads the sensor data, and make sure the python file doesn't contain the loop.",[42,13577,13578],{},"Drag the JSON node onto the canvas and set the action to \"Always convert to JSON object\".",[42,13580,13581],{},"Drag the Debug node onto the canvas.",[42,13583,13584],{},"Connect the output of the Inject node to the input of the Exec node and output of the Exec node to the input of the JSON node, and finally the JSON node's output to the input of the Debug node.",[15,13586,13587],{},"Below is the complete flow which creates the Python file to read the DHT11 sensor and executes that file after 1 second of interval. After deploying the flow you should able to see the sensor data on the debug sidebar as shown in the below image.",[15,13589,13590],{},[165,13591],{"alt":13592,"dataZoomable":103,"src":13593,"title":13594},"\"Image showing the Node-RED flow executing the python script that reads the sensor data\"","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcalling-python-scrpt-from-node-red-output.gif","Image showing the Node-RED flow executing the python script that reads the sensor data",[15,13596,13597,13598,13603],{},"Note: The Python script uses the ",[63,13599,13602],{"href":13600,"rel":13601},"https:\u002F\u002Fdocs.circuitpython.org\u002Fprojects\u002Fdht\u002Fen\u002Flatest\u002Findex.html",[67],"adafruit-circuitpython"," to read the sensor data so make sure to install it. Additionally, the code contained in the template node in the following flow considers that your sensor's signal pin is connected to GPIO 4.",[15,13605,4917,13606,1150],{},[1107,13607,13608,13609,13611,13612,13614,13615,13617,13618,13620,13621,13623,13624,13627,13628,1955,13630,13632,13633,13635,13636,13638,13639,4957],{},"{\"id\":\"94bc6fa766c4b397\",\"type\":\"file\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"filename\":\"~\u002Fsensor.py\",\"filenameType\":\"str\",\"appendNewline\":false,\"createDir\":false,\"overwriteFile\":\"true\",\"encoding\":\"none\",\"x\":610,\"y\":520,\"wires\":[[\"9b08eee57f666de5\"]]},{\"id\":\"37453becdf842bd7\",\"type\":\"template\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"field\":\"payload\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"import time\\nimport board\\nimport adafruit_dht\\nimport json\\n\\ndef publish():\\n    dhtDevice = adafruit_dht.DHT11(board.D4)\\n    try:\\n        temperature_c = dhtDevice.temperature\\n        humidity = dhtDevice.humidity\\n\\n        # Create JSON object\\n        data = {\\n            \"temperature_c\": temperature_c,\\n            \"humidity\": humidity\\n        }\\n\\n        # Convert JSON object to string and print\\n        print(json.dumps(data))\\n\\n    except RuntimeError as error:\\n        print(error.args",[1107,13610,1855],{},")\\n    except Exception as error:\\n        dhtDevice.exit()\\n        raise error\\n    finally:\\n        dhtDevice.exit()\\n\\ndef run():\\n    publish()\\n\\nif ",[19,13613,1914],{}," == '",[19,13616,1797],{},"':\\n    run()\\n\",\"output\":\"str\",\"x\":380,\"y\":520,\"wires\":[[\"94bc6fa766c4b397\"]]},{\"id\":\"5b3642d39c122576\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":880,\"y\":660,\"wires\":",[1107,13619],{},"},{\"id\":\"88144cbc887aada9\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,13622],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":660,\"wires\":[[\"c896267214914641\"]]},{\"id\":\"1b1d792011ffac2c\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":",[1107,13625,13626],{},"{\"p\":\"kill\",\"v\":\"g\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":520,\"wires\":[[\"37453becdf842bd7\"]]},{\"id\":\"c896267214914641\",\"type\":\"exec\",\"z\":\"FFF0000000000001\",\"command\":\"python ~\u002Fsensor.py -u\",\"addpay\":\"payload\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"5\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":400,\"y\":660,\"wires\":[[\"1d02a33a018f0f8d\"],",[1107,13629],{},[1107,13631],{},"]},{\"id\":\"1d02a33a018f0f8d\",\"type\":\"json\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"property\":\"payload\",\"action\":\"\",\"pretty\":false,\"x\":650,\"y\":660,\"wires\":[[\"5b3642d39c122576\"]]},{\"id\":\"9b08eee57f666de5\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":880,\"y\":520,\"wires\":",[1107,13634],{},"},{\"id\":\"343a9f704951f3ed\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Create python file that reads the sensor data\",\"info\":\"\",\"x\":510,\"y\":440,\"wires\":",[1107,13637],{},"},{\"id\":\"216eb1333b2c264c\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Execute the python file that read the data\",\"info\":\"\",\"x\":500,\"y\":580,\"wires\":",[1107,13640],{},[31,13642,13644],{"id":13643},"executing-python-script-with-arguments-from-node-red","Executing Python Script with Arguments from Node-RED",[15,13646,13647],{},"Now, let's revisit our first example. In that example, we executed a simple Python file with a hardcoded value. Now, we'll learn how to pass arguments or inputs to the Python script when executing from Node-RED. For this, we'll need to update the file. Import the following flow, deploy it, and click on the inject button to create the file.",[15,13649,4917,13650,1150],{},[1107,13651,13472,13652,13654,13655,13657,13658],{},[1107,13653,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":300,\"y\":440,\"wires\":[[\"2e1daccf2a7b3d0f\"]]},{\"id\":\"d2d1450deaa588f4\",\"type\":\"file\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"filename\":\".\u002Fexample.py\",\"filenameType\":\"str\",\"appendNewline\":true,\"createDir\":false,\"overwriteFile\":\"true\",\"encoding\":\"none\",\"x\":630,\"y\":440,\"wires\":[[\"e140a8508fb10d96\"]]},{\"id\":\"e140a8508fb10d96\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":820,\"y\":440,\"wires\":",[1107,13656],{},"},{\"id\":\"2e1daccf2a7b3d0f\",\"type\":\"template\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"field\":\"payload\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"import sys\\n\\ndef main():\\n    if len(sys.argv) != 2:\\n        print(\"Usage: python your_script.py ",[13659,13660,13661,13662,13664,13665,13667,13668,13671,13672,13481,13674,13676],"number",{},"\")\\n        return\\n    \\n    user_input = sys.argv",[1107,13663,4944],{},"\\n    \\n    # Check if the input is numeric\\n    if user_input.isdigit() or (user_input",[1107,13666,1855],{}," == '-' and user_input",[1107,13669,13670],{},"1:",".isdigit()):\\n        number = int(user_input)\\n        \\n        # Conditionally render based on the input value\\n        if number \u003C 0:\\n            print(\"Negative number entered\")\\n        elif number == 0:\\n            print(\"Zero entered\")\\n        else:\\n            print(\"Positive number entered\")\\n    else:\\n        print(\"Invalid input. Please enter a valid number.\")\\n\\nif ",[19,13673,1914],{},[19,13675,1797],{},"\":\\n    main()\\n\",\"output\":\"str\",\"x\":460,\"y\":440,\"wires\":[[\"d2d1450deaa588f4\"]]}",[271,13678,13679,13682,13693,13696],{},[42,13680,13681],{},"Drag the Inject node onto the canvas.",[42,13683,13684,13685,13688,13689,13692],{},"Drag the Exec node onto the canvas, set the command to ",[924,13686,13687],{},"python -u .\u002Fexample.py \u003Carg>",", and replace the ",[924,13690,13691],{},"\u003Carg>"," with your argument.",[42,13694,13695],{},"Now Drag the Debug node onto the canvas.",[42,13697,13698],{},"Connect the output of the Inject node to the input of the Exec node, and the output of the Exec node to the input of the Debug node.",[15,13700,13701,13702,13705,13706,13709,13710,13713],{},"If you examine the Python file we've created, you'll notice the use of the 'sys' module, which allows us to read command-line arguments. In our context, we execute the command ",[924,13703,13704],{},"python .\u002Fexample.py -30",". By accessing ",[924,13707,13708],{},"sys.argv[1]",", we retrieve the argument -30. The index 1 is used because ",[924,13711,13712],{},"sys.argv[0]"," provides the filename of the script being executed. Additionally, Python supports passing multiple arguments, so that you can pass as many arguments as you want.",[15,13715,4917,13716,1150],{},[1107,13717,13718,13719,1955,13721,13531,13723,13725,13726,13728,13729,13731,13732,4957],{},"{\"id\":\"2e26b84c0ce17312\",\"type\":\"exec\",\"z\":\"FFF0000000000001\",\"command\":\"python -u .\u002Fexample.py  -30\",\"addpay\":\"\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":520,\"y\":580,\"wires\":[[\"89589c56117004e0\"],",[1107,13720,13527],{},[1107,13722,13530],{},[1107,13724,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":280,\"y\":580,\"wires\":[[\"2e26b84c0ce17312\"]]},{\"id\":\"89589c56117004e0\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Output\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":730,\"y\":540,\"wires\":",[1107,13727],{},"},{\"id\":\"7fdb901b144749c2\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Error\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":730,\"y\":580,\"wires\":",[1107,13730],{},"},{\"id\":\"3f49b49308941782\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"name\":\"Return code\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":750,\"y\":620,\"wires\":",[1107,13733],{},[31,13735,1425],{"id":1424},[15,13737,13738],{},"In this guide, we've demonstrated how to seamlessly execute Python scripts from Node-RED, along with troubleshooting tips and instructions on passing arguments to scripts. By leveraging Python's extensive libraries for data processing, machine learning, and other tasks in conjunction with Node-RED, developers can build powerful IoT solutions with ease.",[2582,13740,13741],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":13743},[13744,13745,13746,13747,13748,13749],{"id":13389,"depth":107,"text":13390},{"id":13399,"depth":107,"text":13400},{"id":13463,"depth":107,"text":13464},{"id":13552,"depth":107,"text":13553},{"id":13643,"depth":107,"text":13644},{"id":1424,"depth":107,"text":1425},{"type":748,"title":13751,"description":13752},"Build Powerful IoT Solutions With Node-RED and Python","FlowFuse gives you a production-ready platform to deploy and manage Node-RED flows, with the flexibility to integrate Python scripts, external libraries, and any data source your project needs. Deploy to edge devices, collaborate with your team, and scale from one device to thousands. Start free today.","2024-07-10","Learn how to run a Python script from Node-RED using the Exec node. Step-by-step guide covering script execution, passing arguments, reading sensor data, and troubleshooting, combine Node-RED's visual flows with Python's libraries.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcalling-python-script-from-node-red.png",{"keywords":13757,"excerpt":13758},"node red and python, node red call python script, node red exec node python script, node red execute python script, node red for python, node red python example, node red python script, node red run python script, node red with python, python in node red",{"type":12,"value":13759},[13760],[15,13761,13386],{},"\u002Fblog\u002F2024\u002F07\u002Fcalling-python-script-from-node-red",{"title":13380,"description":13754},{"loc":13762},"blog\u002F2024\u002F07\u002Fcalling-python-script-from-node-red","A step-by-step guide to executing Python scripts from Node-RED, passing arguments, and capturing output.",[3124,138,13768,7457],"node red python","To run a Python script from Node-RED, use the Exec node with a command like 'python -u .\u002Fyour_script.py'. Trigger it with an Inject node and read the result from a Debug node, the Exec node returns the script's stdout as msg.payload, plus separate outputs for errors and the return code. Add the -u flag to avoid output buffering, pass arguments by appending them to the command (read in Python via sys.argv), and parse printed JSON with a JSON node. Python must be installed on the same machine as Node-RED. This pairs Node-RED's visual flow logic with Python's libraries for tasks like sensor reading, data processing, and machine learning.","X_Vh0vOvtrvtqu01S9PqeFXU8yMsqEy4qHbyK3z1BPA",{"id":13772,"title":11867,"authors":13773,"body":13774,"cta":3,"date":14068,"description":14069,"extension":116,"image":14070,"lastUpdated":14071,"meta":14072,"navigation":129,"path":14085,"seo":14086,"sitemap":14087,"stem":14088,"subtitle":14089,"tags":14090,"tldr":14091,"video":3,"__hash__":14092},"blog\u002Fblog\u002F2024\u002F06\u002Fdashboard-1-deprecated.md",[8417],{"type":12,"value":13775,"toc":14058},[13776,13785,13791,13795,13798,13864,13868,13877,13921,13930,13934,13940,13948,13951,13954,13967,13971,13979,13988,13992,13999,14006,14014,14022,14039,14043,14051],[15,13777,13778,13779,13784],{},"Dave Conway-Jones, the lead maintainer of Node-RED Dashboard, has ",[63,13780,13783],{"href":13781,"rel":13782},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-dashboard-v1-deprecation-notice\u002F89006",[67],"just announced"," that Node-RED Dashboard has been formally deprecated, meaning there will be no further development activity on the project.",[15,13786,13787,13790],{},[63,13788,3465],{"href":3463,"rel":13789},[67]," (also known as Node-RED Dashboard 2.0) is a natural successor to Node-RED Dashboard, and in this article, we detail what FlowFuse Dashboard offers, and how you can get started.",[31,13792,13794],{"id":13793},"whats-new-in-dashboard-20","What's new in Dashboard 2.0?",[15,13796,13797],{},"Not only have we made significant efforts to ensure that FlowFuse Dashboard has as much feature parity with Node-RED Dashboard as possible, but we've also introduced a number of new features that we think you'll love, some highlights include:",[39,13799,13800,13806,13812,13843,13849],{},[42,13801,13802,13805],{},[19,13803,13804],{},"Multi Tenancy:"," A big change from Node-RED Dashboard is the introduction of multi-tenancy features. This allows you to build Dashboards that are unique to each user that interacts with your flows.",[42,13807,13808,13811],{},[19,13809,13810],{},"Responsive Layouts:"," FlowFuse Dashboard is built with a responsive layout in mind, meaning that your Dashboards will automatically adjust to the screen size of the device they are being viewed on.",[42,13813,13814,13817,13818,853,13823,853,13828,853,13833,1413,13838,13842],{},[19,13815,13816],{},"New Nodes:"," We've introduced a five new nodes; ",[63,13819,13822],{"href":13820,"rel":13821},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-button-group.html",[67],"Button Group",[63,13824,13827],{"href":13825,"rel":13826},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-radio-group.html",[67],"Radio Group",[63,13829,13832],{"href":13830,"rel":13831},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-file-input.html",[67],"File Upload",[63,13834,13837],{"href":13835,"rel":13836},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-event.html",[67],"Event",[63,13839,13841],{"href":9144,"rel":13840},[67],"Table"," to help you build your Dashboards, without needing to write custom code, or depend on third-party libraries.",[42,13844,13845,13848],{},[19,13846,13847],{},"Directly Install on Mobile:"," FlowFuse Dashboard is built as a Progressive Web App (PWA), this means that you're able to install it directly onto your mobile device as if it were a native mobile application.",[42,13850,13851,13854,13855,13860,13861,1318],{},[19,13852,13853],{},"Vuetify Framework:"," FlowFuse Dashboard is built on VueJS, and with that, we made the decision to integrate in ",[63,13856,13859],{"href":13857,"rel":13858},"https:\u002F\u002Fvuetifyjs.com\u002Fen\u002Fcomponents\u002Fall\u002F#containment",[67],"Vuetify",", a very rich collection of functional UI widgets. All of these widgets are available, by default when you want to build your own custom widgets in a ",[924,13862,13863],{},"ui-template",[31,13865,13867],{"id":13866},"what-we-have-planned","What we have planned",[15,13869,13870,13871,13876],{},"We are actively investing into the development of FlowFuse Dashboard, and releasing new features and updates on a regular basis. We have a public ",[63,13872,13875],{"href":13873,"rel":13874},"https:\u002F\u002Fgithub.com\u002Forgs\u002FFlowFuse\u002Fprojects\u002F15\u002Fviews\u002F1",[67],"Project Management Board"," that show exactly what we're working on, and what we have planned in the near future. Some particular highlights include:",[39,13878,13879,13889,13910],{},[42,13880,13881,13884,13885,29],{},[19,13882,13883],{},"Visual Layout Editor:"," This would be the biggest improvement to building dashboards in Node-RED. We're aiming for a fully interactive front-end to help you design your Dashboard layouts. This would live in your Dashboard itself, no more needing to switch between the Node-RED Editor and your Dashboard to sanity check changes. You can see more details ",[63,13886,3986],{"href":13887,"rel":13888},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fissues\u002F30",[67],[42,13890,13891,13894,13895,13900,13901,13903,13904,13909],{},[19,13892,13893],{},"Diversity of Data Visualisation:"," We are planning to provide a richer ecosystem of data visualisations. We're also likely to be switching to ",[63,13896,13899],{"href":13897,"rel":13898},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fissues\u002F782",[67],"Apache eCharts"," (subject to further investigation), and are considering a ",[924,13902,13863],{},"-type node that can be used to build ",[63,13905,13908],{"href":13906,"rel":13907},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fissues\u002F58",[67],"custom charts"," too.",[42,13911,13912,13915,13916,29],{},[19,13913,13914],{},"Feature Parity:"," Whilst we've covered there vast majority of Dashboard 1.0 features, we've still got a little bit to go, in particular, the most requested features we have are  ",[63,13917,13920],{"href":13918,"rel":13919},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fissues\u002F555",[67],"programmatic (mustache) title and labels",[15,13922,13923,13924,13929],{},"If there are any other features you'd like to see, please do open a ",[63,13925,13928],{"href":13926,"rel":13927},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-dashboard\u002Fissues\u002Fnew\u002Fchoose",[67],"Feature Request",", and we'll do our best to accommodate.",[31,13931,13933],{"id":13932},"getting-started-with-flowfuse-dashboard","Getting Started with FlowFuse Dashboard",[15,13935,13936,13937,13939],{},"If you're ready to get started with FlowFuse Dashboard, you can install it directly from the Node-RED Palette Manager. Simply search for ",[924,13938,1536],{}," and install the latest version.",[15,13941,13942,13946],{},[165,13943],{"alt":13944,"dataZoomable":103,"src":13945},"A short recording to show how easy it is to setup your first dashboard","https:\u002F\u002Fdashboard.flowfuse.com\u002Fassets\u002Fgetting-started.DHDsIsZl.gif",[170,13947,13944],{},[15,13949,13950],{},"Getting started is as easy as dropping on your first node. FlowFuse Dashboard will automatically set you up with a default group, page, theme and underlying Dashboard configuration.",[399,13952,2689],{"id":13953},"resources",[15,13955,13956,13957,13961,13962,13909],{},"We have a comprehensive ",[63,13958,5894],{"href":13959,"rel":13960},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fgetting-started.html",[67]," that will walk you through the process of setting up your first Dashboard. You can download our ",[63,13963,13966],{"href":13964,"rel":13965},"https:\u002F\u002Fdashboard.flowfuse.com\u002F#download-our-e-book",[67],"\"Ultimate Guide\" eBook",[399,13968,13970],{"id":13969},"dashboard-workshop","Dashboard Workshop",[15,13972,13973,13974,13978],{},"We'll running a ",[63,13975,13977],{"href":13976},"\u002Fwebinars\u002F2024\u002Fworkshop-dashboard","Workshop"," on July 2nd, where we will be covering a collection of useful tips and design patterns to help you get the most out of Dashboard.",[15,13980,13981,13982,13987],{},"The recording will also be available on the ",[63,13983,13986],{"href":13984,"rel":13985},"https:\u002F\u002Fwww.youtube.com\u002F@FlowFuseInc",[67],"FlowFuse YouTube channel"," after the event.",[31,13989,13991],{"id":13990},"migrating-to-flowfuse-dashboard","Migrating to FlowFuse Dashboard",[15,13993,13994,13995,13998],{},"If you're looking to migrate your existing Node-RED Dashboard flows to FlowFuse Dashboard, we have a ",[63,13996,13997],{"href":11879},"Migration Guide"," that details all of the features and properties that have, and haven't, been migrated into FlowFuse Dashboard.",[15,14000,14001,14002,14005],{},"It's worth noting that FlowFuse Dashboard ",[170,14003,14004],{},"will"," run side-by-side with the original Node-RED Dashboard, so you can take your time to migrate your flows over, and do so, piece-by-piece.",[15,14007,14008,14009,992],{},"This week, we also made available the first iteration of the ",[63,14010,14013],{"href":14011,"rel":14012},"https:\u002F\u002Fflowfuse.com\u002Fplatform\u002Fdashboard\u002F#migration-service",[67],"Dashboard Migration Service",[15,14015,14016,14020],{},[165,14017],{"alt":14018,"dataZoomable":103,"src":14019},"Screenshot showing the Dashboard Migration Service available on FlowFuse's website","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fdashboard-migration-service.png",[170,14021,14018],{},[15,14023,14024,14025,853,14028,14031,14032,1413,14035,14038],{},"Currently, it'll convert your ",[924,14026,14027],{},"ui_tab",[924,14029,14030],{},"ui_group"," nodes, and setup your new ",[924,14033,14034],{},"ui-theme",[924,14036,14037],{},"ui-base"," nodes for you. We'll be extending this over time, and eventually hope to auto-convert most of your existing Dashboard 1.0 flows.",[31,14040,14042],{"id":14041},"final-thoughts","Final Thoughts",[15,14044,14045,14046,29],{},"We're really excited to see what you can build with FlowFuse Dashboard, and we're here to help you every step of the way. If you have any questions, please do reach out to us on the ",[63,14047,14050],{"href":14048,"rel":14049},"https:\u002F\u002Fdiscourse.nodered.org\u002Ftag\u002Fdashboard-2",[67],"Node-RED Community Forum",[15,14052,14053,14054,29],{},"If you're interested in contributing and extending FlowFuse Dashboard, we're very open to Pull Requests, and have a detailed contributing guide you can take a look at ",[63,14055,3986],{"href":14056,"rel":14057},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fcontributing\u002F",[67],{"title":103,"searchDepth":104,"depth":104,"links":14059},[14060,14061,14062,14066,14067],{"id":13793,"depth":107,"text":13794},{"id":13866,"depth":107,"text":13867},{"id":13932,"depth":107,"text":13933,"children":14063},[14064,14065],{"id":13953,"depth":294,"text":2689},{"id":13969,"depth":294,"text":13970},{"id":13990,"depth":107,"text":13991},{"id":14041,"depth":107,"text":14042},"2024-06-27","It has just been announced that the predecessor to FlowFuse's Dashboard, Node-RED Dashboard, has been formally deprecated. Find out what this means for your Node-RED instances, see how you can get started with FlowFuse's Dashboard, and what we have planned in the near future.","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fdashboard1-deprecated.png","2026-02-23",{"excerpt":14073},{"type":12,"value":14074},[14075,14080],[15,14076,13778,14077,13784],{},[63,14078,13783],{"href":13781,"rel":14079},[67],[15,14081,14082,13790],{},[63,14083,3465],{"href":3463,"rel":14084},[67],"\u002Fblog\u002F2024\u002F06\u002Fdashboard-1-deprecated",{"title":11867,"description":14069},{"loc":14085},"blog\u002F2024\u002F06\u002Fdashboard-1-deprecated","It has just been announced that the predecessor to FlowFuse's Dashboard, Node-RED Dashboard, has been formally deprecated. Find out how FlowFuse Dashboard can help you build your Dashboards, and what we have planned in the near future.",[8650,136,138,10,12631,3465],"Node-RED Dashboard (Dashboard 1.0) has been formally deprecated, with no further development planned. FlowFuse Dashboard (Dashboard 2.0) is its natural successor, offering multi-tenancy, responsive layouts, new widget nodes, and PWA support. This post covers what's new, what's planned, and how to get started with FlowFuse Dashboard.","166SbqoBIYtzmOfaUFcLssN1_o0slntLT_x9KBUDnNk",{"id":14094,"title":14095,"authors":14096,"body":14097,"cta":14539,"date":14542,"description":14543,"extension":116,"image":14544,"lastUpdated":6203,"meta":14545,"navigation":129,"path":14551,"seo":14552,"sitemap":14553,"stem":14554,"subtitle":14555,"tags":14556,"tldr":14559,"video":3,"__hash__":14560},"blog\u002Fblog\u002F2024\u002F06\u002Finteracting-with-google-sheet-from-node-red.md","Interacting with Google Sheets from Node-RED (2026)",[144],{"type":12,"value":14098,"toc":14527},[14099,14102,14106,14114,14118,14121,14130,14134,14137,14150,14156,14163,14168,14175,14180,14187,14192,14199,14204,14211,14216,14223,14228,14235,14242,14247,14254,14259,14266,14273,14278,14285,14290,14296,14303,14307,14315,14358,14362,14365,14393,14400,14403,14407,14422,14429,14433,14450,14457,14461,14476,14483,14486,14522,14524],[15,14100,14101],{},"Have you ever needed to integrate Google Sheets with your Node-RED application to track and manage data seamlessly? This guide will walk you through the process of integrating Google Sheets with Node-RED, enabling you to write, read, update, and delete data effortlessly.",[31,14103,14105],{"id":14104},"what-is-the-google-sheet","What is the Google Sheet?",[15,14107,14108,14113],{},[63,14109,14112],{"href":14110,"rel":14111},"https:\u002F\u002Fwww.google.com\u002Fsheets\u002Fabout\u002F",[67],"Google Sheets"," is a cloud-based spreadsheet application developed by Google. It allows users to create, edit, and collaborate on spreadsheets in real-time over the Internet. This makes it an ideal option for easily and securely collaborating on data that is not large in size. In businesses, Google Sheets is commonly used for tasks such as analyzing daily profits, tracking expenses, and managing collaborative projects. However, for products or services with large user bases, businesses often prefer using databases, which are recommended for efficiently managing and scaling data operations.",[31,14115,14117],{"id":14116},"prequisite","Prequisite",[15,14119,14120],{},"Before proceeding, make sure you have installed the following node from the pallet manager.",[39,14122,14123],{},[42,14124,14125],{},[63,14126,14129],{"href":14127,"rel":14128},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-google-sheets",[67],"node-red-contrib-google-sheets",[31,14131,14133],{"id":14132},"interacting-with-google-sheets-with-node-red","Interacting with Google Sheets with Node-RED",[15,14135,14136],{},"To integrate Google Sheets with our application we must first enable the Google Sheets API, and create the service account in the Google Cloud, before proceeding, make sure you have the Google Cloud account created.",[271,14138,14139,14147],{},[42,14140,14141,14142,29],{},"Open your browser and go to ",[63,14143,14146],{"href":14144,"rel":14145},"https:\u002F\u002Fconsole.cloud.google.com\u002Fprojectselector2\u002Fapis\u002Flibrary\u002Fsheets?supportedpurview=project&authuser=0",[67],"Service accounts",[42,14148,14149],{},"Create a new project by clicking the \"CREATE PROJECT\" button in the top right corner. Enter the project details such as project name and organization.",[15,14151,14152],{},[165,14153],{"alt":14154,"dataZoomable":103,"src":14155,"title":14154},"Screenshot showing the 'CREATE PROJECT' button","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_1.png",[15,14157,14158],{},[165,14159],{"alt":14160,"dataZoomable":103,"src":14161,"title":14162},"\"Screenshot showing the Form to create the project\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_2.png","Screenshot showing the Form to create the project",[271,14164,14165],{"start":294},[42,14166,14167],{},"Go to the main menu by clicking the menu icon in the top left, then select \"APIs & Services.\"",[15,14169,14170],{},[165,14171],{"alt":14172,"dataZoomable":103,"src":14173,"title":14174},"\"Screenshot showing the 'APIs & Services' from the menu\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_3.png","Screenshot showing the 'APIs & Services' option from the main menu",[271,14176,14177],{"start":104},[42,14178,14179],{},"Click on \"Enable APIs and Services\" in the header.",[15,14181,14182],{},[165,14183],{"alt":14184,"dataZoomable":103,"src":14185,"title":14186},"\"Screenshot showing the 'Enable APIs and Services' option","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_4.png","Screenshot showing the 'Enable APIs and Services' option",[271,14188,14189],{"start":1868},[42,14190,14191],{},"In the search bar, type \"Google Sheets\" and select it from the results.",[15,14193,14194],{},[165,14195],{"alt":14196,"dataZoomable":103,"src":14197,"title":14198},"\"Screenshot showing the Google Sheet in the search result","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_5.png","Screenshot showing the Google Sheet in the search result",[271,14200,14201],{"start":1896},[42,14202,14203],{},"Click the \"Enable\" button to enable the Google Sheets API.",[15,14205,14206],{},[165,14207],{"alt":14208,"dataZoomable":103,"src":14209,"title":14210},"\"Screenshot showing the 'Enable' button\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_6.png","Screenshot showing the 'Enable' button",[271,14212,14213],{"start":1623},[42,14214,14215],{},"Go back to the main menu and click on \"IAM & Admin,\" then select \"Service Accounts\" from the left sidebar.",[15,14217,14218],{},[165,14219],{"alt":14220,"dataZoomable":103,"src":14221,"title":14222},"\"Screenshot showing the 'IAM & Admin' from the menu\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_7.png","Screenshot showing the 'IAM & Admin' from the menu",[271,14224,14225],{"start":1924},[42,14226,14227],{},"Click on \"Create Service Account\" in the header. Enter the necessary details and click \"Create\" to proceed.",[15,14229,14230],{},[165,14231],{"alt":14232,"dataZoomable":103,"src":14233,"title":14234},"\"Screenshot showing the 'Create Service Account' option\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_8.png","Screenshot showing the 'Create Service Account' option",[15,14236,14237],{},[165,14238],{"alt":14239,"dataZoomable":103,"src":14240,"title":14241},"\"Screenshot showing the 'Create' button\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_9.png","Screenshot showing the 'Create' button",[271,14243,14244],{"start":1930},[42,14245,14246],{},"Select the Role from the \"Owner\" and click on the \"Continue\" button.",[15,14248,14249],{},[165,14250],{"alt":14251,"dataZoomable":103,"src":14252,"title":14253},"\"Screenshot showing the 'Continue' button\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_10.png","Screenshot showing the 'Continue' button",[271,14255,14256],{"start":1940},[42,14257,14258],{},"Click \"Done.\" Make sure to copy the generated service account email and save it for later use.",[15,14260,14261],{},[165,14262],{"alt":14263,"dataZoomable":103,"src":14264,"title":14265},"\"Screenshot showing the 'Done' button\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_11.png","Screenshot showing the 'Done' button",[15,14267,14268],{},[165,14269],{"alt":14270,"dataZoomable":103,"src":14271,"title":14272},"\"Screenshot showing the created service account email\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_14.png","Screenshot showing the created service account email",[271,14274,14275],{"start":1976},[42,14276,14277],{},"To generate a private key, click on the three dots icon on the right of the newly created service account and select \"Manage keys.\"",[15,14279,14280],{},[165,14281],{"alt":14282,"dataZoomable":103,"src":14283,"title":14284},"\"Screenshot showing the three dot icon\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_15.png","Screenshot showing the three dot icon",[271,14286,14287],{"start":2002},[42,14288,14289],{},"Click on \"Add key,\" choose \"Create new key,\" select \"JSON\" as the key type, and click \"Create.\" Your private key will be generated and downloaded.",[15,14291,14292],{},[165,14293],{"alt":14294,"dataZoomable":103,"src":14283,"title":14295},"\"Screenshot showing the 'Add key' and the 'Create new key'\"","Screenshot showing the 'Add key' and the 'Create new key'",[15,14297,14298],{},[165,14299],{"alt":14300,"dataZoomable":103,"src":14301,"title":14302},"\"Screenshot showing the 'JSON' option and 'Create new key' button\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete-page_13.png","Screenshot showing the 'JSON' option and 'Create new key' button",[399,14304,14306],{"id":14305},"configuring-the-google-sheet-node","Configuring the Google Sheet Node",[15,14308,14309,14310,14314],{},"Before proceeding, ensure you have added the ",[63,14311,14313],{"href":14312},"\u002Fblog\u002F2023\u002F01\u002Fenvironment-variables-in-node-red\u002F","environment variable"," for the private key that was generated. Additionally, grant the editor access to the sheet you want to interact with for that service account email we created in the above section.",[271,14316,14317,14320,14323,14326,14332,14335],{},[42,14318,14319],{},"Drag a GSheet node onto the canvas.",[42,14321,14322],{},"Double-click on the node and click on the pencil icon next to \"creds.\"",[42,14324,14325],{},"Enter the environment variable added for the private key in the \"creds\" field and click \"Add.\"",[42,14327,14328,14329],{},"Go to the Google Sheet you want to interact with and copy its ID from the URL. The URL will be in this format: ",[924,14330,14331],{},"https:\u002F\u002Fdocs.google.com\u002Fspreadsheets\u002Fd\u002F\u003Cid_of_sheet>\u002F",[42,14333,14334],{},"Return to your Node-RED instance, double-click on the GSheet node again, and paste the spreadsheet ID into the \"SpreadsheetID\" field.",[42,14336,14337,14338,14341,14342,14345,14346,14349,14350,14353,14354,14357],{},"Enter the range of cells you want to work with using the syntax ",[924,14339,14340],{},"\u003Csheetname!first-cell-name:last-cell-name>",". For example, use ",[924,14343,14344],{},"Sheet1!A1:C3"," to specify that you are working with the \"Sheet1\" tab, starting from cell \"A1\" to cell \"C3\". This syntax allows you to define specific ranges such as a row (",[924,14347,14348],{},"A1:A5","), a column (",[924,14351,14352],{},"A1:E1","), or a block (",[924,14355,14356],{},"A1:C3",") within the spreadsheet.",[399,14359,14361],{"id":14360},"writing-data-to-cells","Writing Data to Cells",[15,14363,14364],{},"For demonstration purposes, I will write simulated sensor data which includes a timestamp and sensor data.",[271,14366,14367,14376,14387,14390],{},[42,14368,14369,14370,1242,14372,14375],{},"Drag the Inject node onto the canvas, and set ",[924,14371,1227],{},[924,14373,14374],{},"[$moment().format(), $random() * 100]"," as a JSONata expression, and set it to repeat every 3 seconds of interval.",[42,14377,14378,14379,14382,14383,14386],{},"Double-click on the GSheet node, select the method to \"Append Row\" set the range to ",[924,14380,14381],{},"\u003Csheetname>!A2",", and replace ",[924,14384,14385],{},"sheetname"," with the name of your sheet. I have defined cell A2 because I want to start writing data from cell A2.",[42,14388,14389],{},"Drag the Debug node onto the canvas, which will help in debugging in case of any error.",[42,14391,14392],{},"Connect the output of the Inject node to the input of the GSheet node, and the output of the GSheet node to the input of the Debug node.",[15,14394,14395],{},[165,14396],{"alt":14397,"dataZoomable":103,"src":14398,"title":14399},"\"Image showing the write operation\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-write.gif","Image showing the write operation",[15,14401,14402],{},"This flow generates a timestamp and a random number. The data is formatted as an array because I want the timestamp (the first item of the array) to be placed in column A and the random number (the second item of the array) to be placed in column B. If you want to insert data into additional columns, you can add more items to the array. For example, if you add a third item to the array, it will be placed in column C, a fourth item will be placed in column D, and so on.",[399,14404,14406],{"id":14405},"reading-data-from-cells","Reading Data from Cells",[271,14408,14409,14411,14418,14420],{},[42,14410,13497],{},[42,14412,14413,14414,14417],{},"Drag another GSheet node onto the canvas, and set the method to \"Get Cells\" and the range to ",[924,14415,14416],{},"\u003Csheetname>!A2:C1000",", as I wanted to read data from cell A2 to the next 1000 cells.",[42,14419,13516],{},[42,14421,14392],{},[15,14423,14424],{},[165,14425],{"alt":14426,"dataZoomable":103,"src":14427,"title":14428},"\"Image showing the read operation\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-read.gif","Image showing the read operation",[399,14430,14432],{"id":14431},"updating-data-of-cells","Updating Data of Cells",[271,14434,14435,14440,14446,14448],{},[42,14436,14437,14438,29],{},"Drag an Inject node onto the canvas, and set the updated value as the ",[924,14439,1227],{},[42,14441,14442,14443,14445],{},"Drag another GSheet node onto the canvas, and set the method to \"Update Cells\" and the range to ",[924,14444,14381],{},", as I wanted to update the value of cell A2.",[42,14447,13516],{},[42,14449,14392],{},[15,14451,14452],{},[165,14453],{"alt":14454,"dataZoomable":103,"src":14455,"title":14456},"\"Image showing the update operation\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-update.gif","Image showing the update operation",[399,14458,14460],{"id":14459},"deleting-data-from-cells","Deleting Data from Cells",[271,14462,14463,14465,14472,14474],{},[42,14464,13497],{},[42,14466,14467,14468,14471],{},"Drag another GSheet node onto the canvas, and set the method to \"Clear Cells\" and the range to ",[924,14469,14470],{},"\u003Csheetname>!A2:C50",", as I wanted to clear the first 50 records.",[42,14473,13516],{},[42,14475,14392],{},[15,14477,14478],{},[165,14479],{"alt":14480,"dataZoomable":103,"src":14481,"title":14482},"\"Image showing the delete operation\"","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red-delete.gif","Image showing the delete operation",[15,14484,14485],{},"Below I have provided the complete flow that we have built through the guide, make sure to replace the environment variable with your environment variable added for the private key.",[15,14487,4917,14488,1150],{},[1107,14489,14490,14491,14493,14494,14497,14498,14500,14501,14503,14504,14506,14507,14493,14509,14512,14513,14515,14516,14518,14519,14521],{},"{\"id\":\"7d0282761979574c\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Wrting data to the cells\",\"props\":",[1107,14492,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"",[1107,14495,14496],{},"\\t    $moment(),\\t    $random()*100\\t    \\t","\",\"payloadType\":\"jsonata\",\"x\":240,\"y\":140,\"wires\":[[\"eda23377d98e1a51\"]]},{\"id\":\"eda23377d98e1a51\",\"type\":\"GSheet\",\"z\":\"baa50b8a4762ec1f\",\"creds\":\"d38cb80ae8574ea6\",\"method\":\"append\",\"action\":\"\",\"sheet\":\"1TEEShkuxxrb3WH4NTFyk1COeDyWpgX1w6HN08ZezC7s\",\"cells\":\"Sheet1!A2:C1000\",\"flatten\":false,\"name\":\"\",\"x\":510,\"y\":140,\"wires\":[[\"3e670f575b8227d0\"]]},{\"id\":\"3e670f575b8227d0\",\"type\":\"debug\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":140,\"wires\":",[1107,14499],{},"},{\"id\":\"2c916b1d5c10dffe\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Read the cells data\",\"props\":",[1107,14502],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":210,\"y\":260,\"wires\":[[\"941c7fe7c7dbcbcd\"]]},{\"id\":\"941c7fe7c7dbcbcd\",\"type\":\"GSheet\",\"z\":\"baa50b8a4762ec1f\",\"creds\":\"d38cb80ae8574ea6\",\"method\":\"get\",\"action\":\"\",\"sheet\":\"1TEEShkuxxrb3WH4NTFyk1COeDyWpgX1w6HN08ZezC7s\",\"cells\":\"Sheet1!A2:C3\",\"flatten\":false,\"name\":\"\",\"x\":490,\"y\":260,\"wires\":[[\"f910d7637788361a\"]]},{\"id\":\"f910d7637788361a\",\"type\":\"debug\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":260,\"wires\":",[1107,14505],{},"},{\"id\":\"c20997333f9d4bda\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Updating the cells data\",\"props\":",[1107,14508,5884],{},[1107,14510,14511],{},"\\t   \"none\",\\t   \"none\"\\t    \\t","\",\"payloadType\":\"jsonata\",\"x\":220,\"y\":360,\"wires\":[[\"d9ca2a1e0614f764\"]]},{\"id\":\"d9ca2a1e0614f764\",\"type\":\"GSheet\",\"z\":\"baa50b8a4762ec1f\",\"creds\":\"d38cb80ae8574ea6\",\"method\":\"update\",\"action\":\"\",\"sheet\":\"1TEEShkuxxrb3WH4NTFyk1COeDyWpgX1w6HN08ZezC7s\",\"cells\":\"Sheet1!A35\",\"flatten\":false,\"name\":\"\",\"x\":510,\"y\":360,\"wires\":[[\"9febe629870b7a54\"]]},{\"id\":\"9febe629870b7a54\",\"type\":\"debug\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":360,\"wires\":",[1107,14514],{},"},{\"id\":\"c9ceec9844fa74a9\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Deleting the cells data\",\"props\":",[1107,14517],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":220,\"y\":460,\"wires\":[[\"b9cef9c376d1bea3\"]]},{\"id\":\"b9cef9c376d1bea3\",\"type\":\"GSheet\",\"z\":\"baa50b8a4762ec1f\",\"creds\":\"d38cb80ae8574ea6\",\"method\":\"clear\",\"action\":\"\",\"sheet\":\"1TEEShkuxxrb3WH4NTFyk1COeDyWpgX1w6HN08ZezC7s\",\"cells\":\"Sheet1!A2:C20\",\"flatten\":false,\"name\":\"\",\"x\":500,\"y\":460,\"wires\":[[\"a1766b498efb50f4\"]]},{\"id\":\"a1766b498efb50f4\",\"type\":\"debug\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"debug 4\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":780,\"y\":460,\"wires\":",[1107,14520],{},"},{\"id\":\"d38cb80ae8574ea6\",\"type\":\"gauth\",\"name\":\"Unknown\"}",[31,14523,1425],{"id":1424},[15,14525,14526],{},"This guide demonstrated how to integrate Google Sheets with Node-RED for streamlined data management. We covered setting up the Google Sheets API, configuring Node-RED to interact with sheets, and performing actions like writing, reading, updating, and deleting data.",{"title":103,"searchDepth":104,"depth":104,"links":14528},[14529,14530,14531,14538],{"id":14104,"depth":107,"text":14105},{"id":14116,"depth":107,"text":14117},{"id":14132,"depth":107,"text":14133,"children":14532},[14533,14534,14535,14536,14537],{"id":14305,"depth":294,"text":14306},{"id":14360,"depth":294,"text":14361},{"id":14405,"depth":294,"text":14406},{"id":14431,"depth":294,"text":14432},{"id":14459,"depth":294,"text":14460},{"id":1424,"depth":107,"text":1425},{"type":748,"title":14540,"description":14541},"Connect Node-RED to Any Data Source","FlowFuse makes it simple to build, deploy, and manage Node-RED integrations, from Google Sheets to industrial systems, with production-ready infrastructure and team collaboration built in.","2024-06-21","Learn how to interact with Google Sheet from Node-RED to write, read, update and delete data.","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Finteracting-with-google-sheet-from-node-red.png",{"keywords":14546,"excerpt":14547},"node-red google sheets, node-red-contrib-google-sheets, google sheets api node-red, read write google sheets node-red, google sheets integration, node-red spreadsheet, flowfuse google sheets",{"type":12,"value":14548},[14549],[15,14550,14101],{},"\u002Fblog\u002F2024\u002F06\u002Finteracting-with-google-sheet-from-node-red",{"title":14095,"description":14543},{"loc":14551},"blog\u002F2024\u002F06\u002Finteracting-with-google-sheet-from-node-red","Guide to learn how to write, read, update and delete data in Google sheet using Node-RED.",[8650,138,14557,14558,7457],"node red","google sheet","This guide walks through integrating Google Sheets with Node-RED by enabling the Sheets API, creating a service account in Google Cloud, and configuring the node-red-contrib-google-sheets package. It demonstrates all four CRUD operations, appending rows, reading cell ranges, updating specific cells, and clearing data, using GSheet nodes connected to inject and debug nodes.","wwhZFtf_hiDuN-JVdE6FwPKA7heVvC08rQupvGffhFU",{"id":14562,"title":14563,"authors":14564,"body":14565,"cta":15182,"date":15185,"description":15186,"extension":116,"image":15187,"lastUpdated":6203,"meta":15188,"navigation":129,"path":15194,"seo":15195,"sitemap":15196,"stem":15197,"subtitle":15198,"tags":15199,"tldr":15200,"video":3,"__hash__":15201},"blog\u002Fblog\u002F2024\u002F06\u002Fhow-to-use-mqtt-in-node-red.md","Working with MQTT in Node-RED: Complete Guide (2026)",[144],{"type":12,"value":14566,"toc":15169},[14567,14570,14573,14575,14577,14596,14600,14610,14624,14632,14639,14644,14651,14686,14691,14698,14701,14716,14719,14727,14734,14738,14741,14789,14796,14800,14803,14865,14872,14877,14892,14899,14902,14948,14951,14993,14996,15000,15003,15010,15015,15022,15026,15029,15033,15042,15048,15056,15062,15068,15072,15075,15081,15087,15093,15096,15100,15103,15108,15114,15119,15125,15130,15136,15146,15149,15155,15157,15160,15163,15166],[15,14568,14569],{},"MQTT handles the messaging layer for most IoT deployments. Node-RED provides built-in nodes that connect to MQTT brokers, subscribe to topics, and publish messages, all through a visual interface.",[15,14571,14572],{},"This guide walks through the configuration steps and common patterns you'll need for working implementations.",[31,14574,1491],{"id":1490},[15,14576,1494],{},[39,14578,14579,14590],{},[42,14580,14581,14584,14585,14589],{},[19,14582,14583],{},"Node-RED Instance",": You need Node-RED running somewhere. Easiest option is FlowFuse, grab a ",[63,14586,14588],{"href":3191,"rel":14587},[67],"free trial"," and you get a cloud-hosted instance ready to go. No server setup, no port forwarding hassles.",[42,14591,14592,14595],{},[19,14593,14594],{},"MQTT Broker",": You'll need an MQTT broker to handle message routing. If you're on FlowFuse Pro tier, you get a built-in MQTT broker service, no separate setup needed.",[31,14597,14599],{"id":14598},"getting-connected","Getting Connected",[15,14601,14602,14603,1413,14606,14609],{},"Node-RED ships with MQTT nodes already installed. Open your Node-RED editor and look in the palette, you'll find ",[19,14604,14605],{},"mqtt-in",[19,14607,14608],{},"mqtt-out"," under the network section.",[259,14611,14612],{},[15,14613,14614,14615,1413,14619,14623],{},"If you're using FlowFuse's managed MQTT broker, this is straightforward. Use the ",[63,14616,14618],{"href":14617},"\u002Fnode-red\u002Fflowfuse\u002Fmqtt\u002Fmqtt-in\u002F","ff-mqtt-in",[63,14620,14622],{"href":14621},"\u002Fnode-red\u002Fflowfuse\u002Fmqtt\u002Fmqtt-out\u002F","ff-mqtt-out"," nodes instead of the standard MQTT nodes. Simply drag one onto the canvas, and the connection to FlowFuse's broker will be configured automatically.",[15,14625,14626,14627,2642,14629,14631],{},"For any other broker, drag an ",[19,14628,14605],{},[19,14630,14608],{}," node onto your workspace. Double-click it to open the configuration panel.",[15,14633,14634,14635,14638],{},"Click the pencil icon next to ",[19,14636,14637],{},"Server",". You'll see two tabs: Connection and Security.",[15,14640,14641],{},[19,14642,14643],{},"Connection Tab",[15,14645,14646],{},[165,14647],{"alt":14648,"dataZoomable":103,"src":14649,"title":14650},"MQTT broker connection configuration screen showing server address, port, and protocol settings","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-broker-config.png","MQTT Connection Tab - Configure broker address, port, and TLS settings",[271,14652,14653,14662,14674,14680],{},[42,14654,14655,14657,14658,14661],{},[19,14656,14637],{},": Enter your broker address (",[924,14659,14660],{},"broker.hivemq.com"," for testing, or your broker's hostname)",[42,14663,14664,14667,14668,14670,14671,14673],{},[19,14665,14666],{},"Port",": Use ",[924,14669,10475],{}," for encrypted connections, ",[924,14672,10471],{}," for unencrypted",[42,14675,14676,14679],{},[19,14677,14678],{},"Enable TLS",": Check this if you're using port 8883",[42,14681,14682,14685],{},[19,14683,14684],{},"Protocol",": Leave it at MQTT 3.1.1 unless you specifically need MQTT 5 features",[15,14687,14688],{},[19,14689,14690],{},"Security Tab",[15,14692,14693],{},[165,14694],{"alt":14695,"dataZoomable":103,"src":14696,"title":14697},"MQTT broker security configuration showing username and password fields","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-configuration-security-tab.png","MQTT Security Tab - Enter credentials using environment variables for security",[15,14699,14700],{},"Enter your username and password. For anything beyond local testing, use environment variables:",[39,14702,14703,14710],{},[42,14704,14705,14706,14709],{},"Put ",[924,14707,14708],{},"${MQTT_USER}"," in the username field",[42,14711,14705,14712,14715],{},[924,14713,14714],{},"${MQTT_PASSWORD}"," in the password field",[15,14717,14718],{},"Set these in your Node-RED settings or FlowFuse environment config",[259,14720,14721],{},[15,14722,2980,14723,14726],{},[63,14724,14725],{"href":14312},"Using Environment Variables in Node-RED"," for setup details.",[15,14728,297,14729,2450,14731,14733],{},[19,14730,2449],{},[19,14732,2453],{},". Deploy your flow. If the node shows \"connected\" under it, you're in.",[31,14735,14737],{"id":14736},"publishing-data-to-a-topic-on-mqtt-broker","Publishing Data to a Topic on MQTT Broker",[15,14739,14740],{},"The mqtt-out node sends data from Node-RED to your MQTT broker. Configure the topic, quality of service level, and retention settings to control how your messages are delivered.",[271,14742,14743,14747,14755,14763,14777,14783],{},[42,14744,1045,14745,1216],{},[19,14746,14608],{},[42,14748,1641,14749,14751,14752,14754],{},[19,14750,14608],{}," node and select your broker from the ",[19,14753,14637],{}," dropdown.",[42,14756,14757,14758,14760,14761,29],{},"Enter your topic name in the ",[19,14759,8623],{}," field. Use forward slashes for hierarchy like ",[924,14762,9881],{},[42,14764,1224,14765,1242,14768,14770,14771,14773,14774,14776],{},[19,14766,14767],{},"QoS",[19,14769,4948],{}," for guaranteed delivery. Use ",[19,14772,4944],{}," if you can tolerate occasional duplicates, or ",[19,14775,1855],{}," for high-frequency data where missing readings don't matter.",[42,14778,4147,14779,14782],{},[19,14780,14781],{},"Retain"," if new subscribers should immediately receive the last published value.",[42,14784,14785,14786,14788],{},"Connect your data source to the mqtt-out node's input. The node expects ",[924,14787,1227],{}," to contain your data.",[15,14790,14791],{},[165,14792],{"alt":14793,"dataZoomable":103,"src":14794,"title":14795},"MQTT-out node configuration panel settings","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-out.png","MQTT-out Node Configuration",[31,14797,14799],{"id":14798},"subscribing-to-a-topic-on-mqtt-broker","Subscribing to a Topic on MQTT Broker",[15,14801,14802],{},"The mqtt-in node receives messages from topics you specify. You can subscribe to specific topics or use wildcards to monitor multiple topics at once.",[271,14804,14805,14809,14815,14847,14853,14862],{},[42,14806,1045,14807,1216],{},[19,14808,14605],{},[42,14810,1641,14811,14751,14813,14754],{},[19,14812,14605],{},[19,14814,14637],{},[42,14816,1224,14817,1242,14819,14822,14823,14825,14826],{},[19,14818,8610],{},[19,14820,14821],{},"subscribe to a single topic"," and enter the topic name in the ",[19,14824,8623],{}," field. You can use:\n",[39,14827,14828,14833,14840],{},[42,14829,14830,14831],{},"Specific topics: ",[924,14832,9881],{},[42,14834,14835,14836,14839],{},"Single-level wildcard: ",[924,14837,14838],{},"factory\u002F+\u002Ftemp"," (matches any value where + appears)",[42,14841,14842,14843,14846],{},"Multi-level wildcard: ",[924,14844,14845],{},"factory\u002F#"," (matches everything under factory\u002F)",[42,14848,1224,14849,1242,14851,29],{},[19,14850,14767],{},[19,14852,4948],{},[42,14854,1224,14855,1242,14858,14861],{},[19,14856,14857],{},"Output",[19,14859,14860],{},"auto-detect"," (it parses JSON automatically and passes through other formats as strings).",[42,14863,14864],{},"Connect the mqtt-in node's output to wherever you need the data.",[15,14866,14867],{},[165,14868],{"alt":14869,"dataZoomable":103,"src":14870,"title":14871},"MQTT-in node configuration showing subscription settings with topic field and QoS options","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-in-config.png","MQTT-in Node Configuration",[15,14873,14874],{},[19,14875,14876],{},"For Dynamic Subscriptions:",[15,14878,14879,14880,1242,14882,14885,14886,1413,14888,14891],{},"If you need to change subscriptions at runtime based on user input or system conditions, set ",[19,14881,8610],{},[19,14883,14884],{},"dynamic"," instead. You'll control subscriptions by sending messages with ",[924,14887,1241],{},[924,14889,14890],{},"msg.action"," to the node's input.",[15,14893,14894],{},[165,14895],{"alt":14896,"dataZoomable":103,"src":14897,"title":14898},"MQTT-in node configured for dynamic subscription mode with empty topic field","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-in-dynamic.png","MQTT-in Dynamic Mode",[15,14900,14901],{},"To subscribe dynamically, send a message like:",[1756,14903,14905],{"className":1758,"code":14904,"language":1760,"meta":103,"style":103},"msg.topic = \"factory\u002Fline1\u002Ftemperature\";\nmsg.action = \"subscribe\";\n",[924,14906,14907,14928],{"__ignoreMap":103},[1107,14908,14909,14912,14914,14917,14919,14921,14924,14926],{"class":1765,"line":1766},[1107,14910,14911],{"class":1779},"msg",[1107,14913,29],{"class":1783},[1107,14915,14916],{"class":1779},"topic ",[1107,14918,1784],{"class":1783},[1107,14920,1963],{"class":1783},[1107,14922,14923],{"class":1966},"factory\u002Fline1\u002Ftemperature",[1107,14925,1970],{"class":1783},[1107,14927,1805],{"class":1783},[1107,14929,14930,14932,14934,14937,14939,14941,14944,14946],{"class":1765,"line":107},[1107,14931,14911],{"class":1779},[1107,14933,29],{"class":1783},[1107,14935,14936],{"class":1779},"action ",[1107,14938,1784],{"class":1783},[1107,14940,1963],{"class":1783},[1107,14942,14943],{"class":1966},"subscribe",[1107,14945,1970],{"class":1783},[1107,14947,1805],{"class":1783},[15,14949,14950],{},"To unsubscribe:",[1756,14952,14954],{"className":1758,"code":14953,"language":1760,"meta":103,"style":103},"msg.topic = \"factory\u002Fline1\u002Ftemperature\";\nmsg.action = \"unsubscribe\";\n",[924,14955,14956,14974],{"__ignoreMap":103},[1107,14957,14958,14960,14962,14964,14966,14968,14970,14972],{"class":1765,"line":1766},[1107,14959,14911],{"class":1779},[1107,14961,29],{"class":1783},[1107,14963,14916],{"class":1779},[1107,14965,1784],{"class":1783},[1107,14967,1963],{"class":1783},[1107,14969,14923],{"class":1966},[1107,14971,1970],{"class":1783},[1107,14973,1805],{"class":1783},[1107,14975,14976,14978,14980,14982,14984,14986,14989,14991],{"class":1765,"line":107},[1107,14977,14911],{"class":1779},[1107,14979,29],{"class":1783},[1107,14981,14936],{"class":1779},[1107,14983,1784],{"class":1783},[1107,14985,1963],{"class":1783},[1107,14987,14988],{"class":1966},"unsubscribe",[1107,14990,1970],{"class":1783},[1107,14992,1805],{"class":1783},[15,14994,14995],{},"This is useful when users select which equipment to monitor from a dashboard, when topics depend on database queries or API responses, or when you need to add and remove subscriptions without redeploying your flow.",[31,14997,14999],{"id":14998},"deploying-the-flow","Deploying the Flow",[15,15001,15002],{},"Deploy your flow to activate the MQTT connection and start sending or receiving messages.",[271,15004,15005],{},[42,15006,278,15007,15009],{},[19,15008,1330],{}," button at the top-right corner.",[15,15011,15012],{},[170,15013,15014],{},"Tip: Check the node status below each MQTT node. It will display 'connected' if the connection is successful.",[15,15016,15017],{},[165,15018],{"alt":15019,"dataZoomable":103,"src":15020,"title":15021},"MQTT nodes in Node-RED editor showing green connected status indicators below each node","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fmqtt-node-status.png","MQTT Node Status - Green 'connected' indicator confirms successful broker connection",[31,15023,15025],{"id":15024},"best-practices","Best Practices",[15,15027,15028],{},"Production MQTT deployments need more than basic configuration. These practices separate hobby projects from industrial-grade systems that run 24\u002F7 without issues.",[399,15030,15032],{"id":15031},"security-first","Security First",[15,15034,15035,15038,15039,15041],{},[19,15036,15037],{},"Always use TLS encryption."," Unencrypted MQTT sends credentials and data in plain text across your network. Enable port 8883 with TLS on your broker, then check the ",[19,15040,14678],{}," box in your Node-RED broker configuration. No exceptions for production systems.",[15,15043,15044,15047],{},[19,15045,15046],{},"Never hardcode credentials."," Putting usernames and passwords directly in nodes means anyone with access to your flows can see them. Use environment variables instead:",[1756,15049,15054],{"className":15050,"code":15052,"language":15053},[15051],"language-text","Username: ${MQTT_USER}\nPassword: ${MQTT_PASS}\n","text",[924,15055,15052],{"__ignoreMap":103},[15,15057,15058,15059,15061],{},"Set these in your Node-RED settings file or FlowFuse environment configuration. See ",[63,15060,14725],{"href":14312}," for the complete setup.",[15,15063,15064,15067],{},[19,15065,15066],{},"Implement access control on your broker."," Not every device needs to publish and subscribe to every topic. Configure your MQTT broker's ACL (Access Control List) to restrict clients based on their role. Sensors should only publish to their specific topics. Dashboard applications should only subscribe to what they display. This limits damage if credentials get compromised.",[399,15069,15071],{"id":15070},"choose-the-right-qos","Choose the Right QoS",[15,15073,15074],{},"QoS isn't a \"set it and forget it\" decision. Each level trades reliability against network overhead and latency.",[15,15076,15077,15080],{},[19,15078,15079],{},"QoS 0:"," Fire and forget. Use for high-frequency sensor data where missing one reading doesn't matter.",[15,15082,15083,15086],{},[19,15084,15085],{},"QoS 1:"," Guaranteed delivery, possible duplicates. Good for status updates and notifications.",[15,15088,15089,15092],{},[19,15090,15091],{},"QoS 2:"," Exactly once delivery with higher latency. Use for commands that trigger actions or critical transactions.",[15,15094,15095],{},"Match QoS to your use case. Don't default everything to QoS 2 just to be safe, you'll waste bandwidth and add latency where it doesn't help.",[399,15097,15099],{"id":15098},"design-your-topic-hierarchy-properly","Design Your Topic Hierarchy Properly",[15,15101,15102],{},"Your topic structure determines how easy it is to filter, subscribe, and scale your system. A hierarchical approach mirrors your physical setup and makes wildcards useful.",[15,15104,15105],{},[19,15106,15107],{},"Structure topics from general to specific:",[1756,15109,15112],{"className":15110,"code":15111,"language":15053},[15051],"company\u002Flocation\u002Farea\u002Fequipment\u002Fmeasurement\n",[924,15113,15111],{"__ignoreMap":103},[15,15115,15116],{},[19,15117,15118],{},"Good hierarchy:",[1756,15120,15123],{"className":15121,"code":15122,"language":15053},[15051],"acme\u002Fchicago\u002Fpackaging\u002Ffiller-01\u002Ftemperature\nacme\u002Fchicago\u002Fpackaging\u002Ffiller-01\u002Fpressure\nacme\u002Fdenver\u002Fassembly\u002Frobot-03\u002Ftemperature\nacme\u002Fdenver\u002Fassembly\u002Fconveyor-02\u002Fspeed\n",[924,15124,15122],{"__ignoreMap":103},[15,15126,15127],{},[19,15128,15129],{},"Poor hierarchy:",[1756,15131,15134],{"className":15132,"code":15133,"language":15053},[15051],"temp-sensor-1\ntemp-sensor-2\npressure-sensor-1\nline1-data\n",[924,15135,15133],{"__ignoreMap":103},[15,15137,15138,15139,15142,15143,29],{},"The difference: with a proper hierarchy, you can subscribe at any level. Need all data from Chicago? Subscribe to ",[924,15140,15141],{},"acme\u002Fchicago\u002F#",". Want temperature readings across all sites? Use ",[924,15144,15145],{},"acme\u002F+\u002F+\u002F+\u002Ftemperature",[15,15147,15148],{},"Flat topics force you to subscribe to dozens of individual topics and manage them manually. Hierarchical topics let the broker do the filtering.",[15,15150,15151,15154],{},[19,15152,15153],{},"Keep it simple:"," Use lowercase, separate levels with forward slashes, use hyphens within names. Topics are paths, not sentences.",[31,15156,1425],{"id":1424},[15,15158,15159],{},"You now know how to connect Node-RED to MQTT brokers, publish data to topics, subscribe to incoming messages, and secure your deployment with TLS and proper credentials. That's the foundation.",[15,15161,15162],{},"Go build something. Connect a sensor, route the data, display it somewhere useful. The patterns you've learned here scale from a single device to entire factory floors.",[15,15164,15165],{},"When you hit the limits of managing individual instances (tracking deployments, coordinating updates, maintaining uptime across sites), that's when tools like [FlowFuse]({% include \"sign-up-url.njk\" %}) become relevant. Until then, you have everything you need.",[2582,15167,15168],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":15170},[15171,15172,15173,15174,15175,15176,15181],{"id":1490,"depth":107,"text":1491},{"id":14598,"depth":107,"text":14599},{"id":14736,"depth":107,"text":14737},{"id":14798,"depth":107,"text":14799},{"id":14998,"depth":107,"text":14999},{"id":15024,"depth":107,"text":15025,"children":15177},[15178,15179,15180],{"id":15031,"depth":294,"text":15032},{"id":15070,"depth":294,"text":15071},{"id":15098,"depth":294,"text":15099},{"id":1424,"depth":107,"text":1425},{"type":748,"title":15183,"description":15184},"Run Production-Grade MQTT and Node-RED in One Platform","FlowFuse gives you hosted Node-RED and a built-in MQTT broker with access control, TLS, and managed credentials, plus remote device management and DevOps pipelines to run reliably from one device to entire factory floors. Start free today.","2024-06-05","Complete MQTT Node-RED tutorial: configure brokers, implement pub\u002Fsub messaging, use mqtt-in and mqtt-out nodes, and create dynamic subscriptions for IoT","\u002Fblog\u002F2024\u002F06\u002Fimages\u002Fworking-with-mqtt.jpg",{"keywords":15189,"excerpt":15190},"node red mqtt, mqtt node red, mqtt in node red, node red mqtt in, node red mqtt out, node red mqtt broker, mqtt broker node red, node-red-contrib-mqtt-broker, node red mqtt dynamic subscription, node red mqtt example, mqtt dynamic subscription, mqtt in\u002Fout nodes, mqtt broker setup",{"type":12,"value":15191},[15192],[15,15193,14569],{},"\u002Fblog\u002F2024\u002F06\u002Fhow-to-use-mqtt-in-node-red",{"title":14563,"description":15186},{"loc":15194},"blog\u002F2024\u002F06\u002Fhow-to-use-mqtt-in-node-red","Connect, subscribe, and publish MQTT messages in Node-RED",[8650,138,8410,7457],"Node-RED's built-in mqtt-in and mqtt-out nodes make it straightforward to connect to any MQTT broker, subscribe to topics with wildcard support, and publish data. Dynamic subscriptions, TLS security, environment-variable credentials, and a well-structured topic hierarchy are the key practices for reliable, production-grade MQTT flows.","MM_Ln2w_vqrUlQzGO9sjEvDGcCW4gg9usLAqHHMBUxk",{"id":15203,"title":15204,"authors":15205,"body":15206,"cta":16467,"date":16470,"description":16471,"extension":116,"image":16472,"lastUpdated":16473,"meta":16474,"navigation":129,"path":16531,"seo":16532,"sitemap":16533,"stem":16534,"subtitle":16535,"tags":16536,"tldr":16537,"video":3,"__hash__":16538},"blog\u002Fblog\u002F2024\u002F05\u002Funderstanding-node-flow-global-environment-variables-in-node-red.md","How to Use Variables in Node-RED: Flow, Global, Context & Environment (2026)",[144],{"type":12,"value":15207,"toc":16446},[15208,15257,15260,15264,15267,15270,15284,15290,15296,15300,15303,15309,15313,15319,15322,15333,15340,15343,15413,15419,15422,15429,15432,15493,15499,15506,15510,15513,15519,15523,15528,15531,15538,15540,15597,15602,15605,15612,15614,15673,15679,15686,15690,15693,15699,15703,15706,15711,15773,15778,15842,15848,15852,15855,15858,15862,15867,15870,15875,15878,15986,15991,15994,16001,16003,16113,16116,16120,16123,16130,16135,16152,16155,16162,16166,16169,16172,16178,16184,16188,16193,16207,16214,16220,16225,16228,16235,16237,16299,16302,16331,16338,16348,16354,16356,16359,16364,16378,16393,16399,16405,16411,16417,16419,16437,16440,16443],[15,15209,15210,15211,15214,15215,15217,15218,15221,15222,15225,15226,15229,15230,15233,15234,15237,15238,810,15241,853,15244,810,15247,4972,15250,810,15253,15256],{},"Node-RED has four types of variables: ",[19,15212,15213],{},"message variables"," that travel with ",[924,15216,14911],{}," through your nodes, and ",[19,15219,15220],{},"context variables"," in three scopes, ",[19,15223,15224],{},"node"," (private to a single function node), ",[19,15227,15228],{},"flow"," (shared within one tab), and ",[19,15231,15232],{},"global"," (shared across the entire instance), plus ",[19,15235,15236],{},"environment variables"," for configuration and secrets. You set and retrieve context variables with ",[924,15239,15240],{},"global.set()",[924,15242,15243],{},"global.get()",[924,15245,15246],{},"flow.set()",[924,15248,15249],{},"flow.get()",[924,15251,15252],{},"context.set()",[924,15254,15255],{},"context.get()"," in function nodes, or visually through the change node. Context variables are stored in memory by default and are lost on restart, so any state that must survive a reboot needs persistent storage.",[15,15258,15259],{},"That's the short version. The rest of this guide walks through each variable type in detail, showing you exactly how to set, retrieve, and delete them, when to use each scope, and how to make them persist across restarts and redeployments.",[31,15261,15263],{"id":15262},"what-are-node-red-variables","What Are Node-RED Variables?",[15,15265,15266],{},"Variables in Node-RED serve as containers for storing and managing data throughout your application. Understanding the different types and their scopes is essential for building efficient, organized flows.",[15,15268,15269],{},"Node-RED offers three primary variable categories:",[15,15271,15272,15275,15276,15278,15279,15283],{},[19,15273,15274],{},"Message variables"," travel with the message object as it flows through your nodes. The most common example is ",[924,15277,1227],{},", which carries the primary data between nodes. For a deeper dive into message handling, see the ",[63,15280,15282],{"href":15281},"\u002Fnode-red\u002Fgetting-started\u002Fnode-red-messages\u002F","Understanding Node-RED Messages"," guide.",[15,15285,15286,15289],{},[19,15287,15288],{},"Context variables"," store application state at different levels, node, flow, or global scope. They persist data that needs to be accessed across multiple message events, making them ideal for tracking counters, storing configuration, or maintaining state.",[15,15291,15292,15295],{},[19,15293,15294],{},"Environment variables"," handle configuration data and sensitive information like API keys and credentials. By storing this data separately from your flows, you maintain security and make configuration management more flexible.",[31,15297,15299],{"id":15298},"global-variables-instance-wide-data-storage","Global Variables: Instance-Wide Data Storage",[15,15301,15302],{},"Global variables provide a centralized storage mechanism accessible throughout your entire Node-RED instance. Any function, change, inject, or switch node can read or write global variables, making them perfect for sharing data across multiple flows.",[15,15304,15305,15308],{},[19,15306,15307],{},"When to use global variables:"," Consider using them for system-wide settings, shared configuration, or data that multiple flows need to access. For example, in a home automation system with separate flows for lighting, security, and climate control, global variables can store user preferences that all flows reference.",[399,15310,15312],{"id":15311},"working-with-global-variables","Working with Global Variables",[15,15314,15315,15318],{},[19,15316,15317],{},"Setting global variables"," can be done through the change node or programmatically in a function node:",[15,15320,15321],{},"Using the change node:",[271,15323,15324,15327,15330],{},[42,15325,15326],{},"Select \"global\" from the variable type dropdown",[42,15328,15329],{},"Enter your variable name",[42,15331,15332],{},"Set the value or expression",[15,15334,15335],{},[165,15336],{"alt":15337,"dataZoomable":103,"src":15338,"title":15339},"\"Screenshot showing how to set global variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-setting-global-variable-using-change-node.png","Screenshot showing how to set global variable using the change node",[15,15341,15342],{},"Using a function node:",[1756,15344,15346],{"className":1758,"code":15345,"language":1760,"meta":103,"style":103},"global.set('userName', 'John');\nglobal.set('systemMode', 'active');\n",[924,15347,15348,15383],{"__ignoreMap":103},[1107,15349,15350,15352,15354,15358,15361,15364,15367,15369,15371,15374,15377,15379,15381],{"class":1765,"line":1766},[1107,15351,15232],{"class":1779},[1107,15353,29],{"class":1783},[1107,15355,15357],{"class":15356},"s2Zo4","set",[1107,15359,15360],{"class":1779},"(",[1107,15362,15363],{"class":1783},"'",[1107,15365,15366],{"class":1966},"userName",[1107,15368,15363],{"class":1783},[1107,15370,1955],{"class":1783},[1107,15372,15373],{"class":1783}," '",[1107,15375,15376],{"class":1966},"John",[1107,15378,15363],{"class":1783},[1107,15380,8879],{"class":1779},[1107,15382,1805],{"class":1783},[1107,15384,15385,15387,15389,15391,15393,15395,15398,15400,15402,15404,15407,15409,15411],{"class":1765,"line":107},[1107,15386,15232],{"class":1779},[1107,15388,29],{"class":1783},[1107,15390,15357],{"class":15356},[1107,15392,15360],{"class":1779},[1107,15394,15363],{"class":1783},[1107,15396,15397],{"class":1966},"systemMode",[1107,15399,15363],{"class":1783},[1107,15401,1955],{"class":1783},[1107,15403,15373],{"class":1783},[1107,15405,15406],{"class":1966},"active",[1107,15408,15363],{"class":1783},[1107,15410,8879],{"class":1779},[1107,15412,1805],{"class":1783},[15,15414,15415,15418],{},[19,15416,15417],{},"Retrieving global variables"," follows a similar pattern:",[15,15420,15421],{},"In a change, inject, or switch node, simply set the action to “set”, choose the type as “global”, and specify the variable name.",[15,15423,15424],{},[165,15425],{"alt":15426,"dataZoomable":103,"src":15427,"title":15428},"\"Screenshot showing how to retrieve global variables using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-retrieving-global-variable-using-change-node.png","Screenshot showing how to retrieve global variables using the change node",[15,15430,15431],{},"In function nodes:",[1756,15433,15435],{"className":1758,"code":15434,"language":1760,"meta":103,"style":103},"const userName = global.get('userName');\nconst mode = global.get('systemMode');\n",[924,15436,15437,15466],{"__ignoreMap":103},[1107,15438,15439,15441,15444,15446,15449,15451,15454,15456,15458,15460,15462,15464],{"class":1765,"line":1766},[1107,15440,1776],{"class":1775},[1107,15442,15443],{"class":1779}," userName ",[1107,15445,1784],{"class":1783},[1107,15447,15448],{"class":1779}," global",[1107,15450,29],{"class":1783},[1107,15452,15453],{"class":15356},"get",[1107,15455,15360],{"class":1779},[1107,15457,15363],{"class":1783},[1107,15459,15366],{"class":1966},[1107,15461,15363],{"class":1783},[1107,15463,8879],{"class":1779},[1107,15465,1805],{"class":1783},[1107,15467,15468,15470,15473,15475,15477,15479,15481,15483,15485,15487,15489,15491],{"class":1765,"line":107},[1107,15469,1776],{"class":1775},[1107,15471,15472],{"class":1779}," mode ",[1107,15474,1784],{"class":1783},[1107,15476,15448],{"class":1779},[1107,15478,29],{"class":1783},[1107,15480,15453],{"class":15356},[1107,15482,15360],{"class":1779},[1107,15484,15363],{"class":1783},[1107,15486,15397],{"class":1966},[1107,15488,15363],{"class":1783},[1107,15490,8879],{"class":1779},[1107,15492,1805],{"class":1783},[15,15494,15495,15498],{},[19,15496,15497],{},"Deleting global variables"," can be accomplished through the change node by selecting \"delete\" from the action dropdown, or via the Context Data sidebar panel, which provides a comprehensive view of all variables.",[15,15500,15501],{},[165,15502],{"alt":15503,"dataZoomable":103,"src":15504,"title":15505},"\"Screenshot showing how to delete global variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-deleting-global-variable-using-change-node.png","Screenshot showing how to delete global variable using the change node",[31,15507,15509],{"id":15508},"flow-variables-tab-scoped-data","Flow Variables: Tab-Scoped Data",[15,15511,15512],{},"Flow variables exist within a single tab or flow in your Node-RED editor. They're accessible to all nodes within that specific flow but isolated from other flows, providing logical data separation.",[15,15514,15515,15518],{},[19,15516,15517],{},"When to use flow variables:"," Use them for data that's relevant only to a specific workflow. For instance, in a temperature monitoring flow with multiple sensor nodes, flow variables can track the current reading, alert thresholds, or calculation results, data that doesn't need to be shared with other parts of your application.",[399,15520,15522],{"id":15521},"working-with-flow-variables","Working with Flow Variables",[15,15524,15525],{},[19,15526,15527],{},"Setting flow variables:",[15,15529,15530],{},"Using the change node, select the action “set”, choose “flow” as the variable type, and configure your variable.",[15,15532,15533],{},[165,15534],{"alt":15535,"dataZoomable":103,"src":15536,"title":15537},"\"Screenshot showing how to set flow variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-setting-flow-variable-using-change-node.png","Screenshot showing how to set flow variable using the change node",[15,15539,15431],{},[1756,15541,15543],{"className":1758,"code":15542,"language":1760,"meta":103,"style":103},"flow.set('currentTemp', 72.5);\nflow.set('alertThreshold', 85);\n",[924,15544,15545,15571],{"__ignoreMap":103},[1107,15546,15547,15549,15551,15553,15555,15557,15560,15562,15564,15567,15569],{"class":1765,"line":1766},[1107,15548,15228],{"class":1779},[1107,15550,29],{"class":1783},[1107,15552,15357],{"class":15356},[1107,15554,15360],{"class":1779},[1107,15556,15363],{"class":1783},[1107,15558,15559],{"class":1966},"currentTemp",[1107,15561,15363],{"class":1783},[1107,15563,1955],{"class":1783},[1107,15565,15566],{"class":1854}," 72.5",[1107,15568,8879],{"class":1779},[1107,15570,1805],{"class":1783},[1107,15572,15573,15575,15577,15579,15581,15583,15586,15588,15590,15593,15595],{"class":1765,"line":107},[1107,15574,15228],{"class":1779},[1107,15576,29],{"class":1783},[1107,15578,15357],{"class":15356},[1107,15580,15360],{"class":1779},[1107,15582,15363],{"class":1783},[1107,15584,15585],{"class":1966},"alertThreshold",[1107,15587,15363],{"class":1783},[1107,15589,1955],{"class":1783},[1107,15591,15592],{"class":1854}," 85",[1107,15594,8879],{"class":1779},[1107,15596,1805],{"class":1783},[15,15598,15599],{},[19,15600,15601],{},"Retrieving flow variables:",[15,15603,15604],{},"In a change, inject, or switch node, simply set the action to “set”, choose the type as “flow”, and specify the variable name.",[15,15606,15607],{},[165,15608],{"alt":15609,"dataZoomable":103,"src":15610,"title":15611},"\"Screenshot showing how to retrieve flow variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-retrieving-flow-variable-using-change-node.png","Screenshot showing how to retrieve flow variable using the change node",[15,15613,15431],{},[1756,15615,15617],{"className":1758,"code":15616,"language":1760,"meta":103,"style":103},"const temp = flow.get('currentTemp');\nconst threshold = flow.get('alertThreshold');\n",[924,15618,15619,15646],{"__ignoreMap":103},[1107,15620,15621,15623,15625,15627,15630,15632,15634,15636,15638,15640,15642,15644],{"class":1765,"line":1766},[1107,15622,1776],{"class":1775},[1107,15624,1780],{"class":1779},[1107,15626,1784],{"class":1783},[1107,15628,15629],{"class":1779}," flow",[1107,15631,29],{"class":1783},[1107,15633,15453],{"class":15356},[1107,15635,15360],{"class":1779},[1107,15637,15363],{"class":1783},[1107,15639,15559],{"class":1966},[1107,15641,15363],{"class":1783},[1107,15643,8879],{"class":1779},[1107,15645,1805],{"class":1783},[1107,15647,15648,15650,15653,15655,15657,15659,15661,15663,15665,15667,15669,15671],{"class":1765,"line":107},[1107,15649,1776],{"class":1775},[1107,15651,15652],{"class":1779}," threshold ",[1107,15654,1784],{"class":1783},[1107,15656,15629],{"class":1779},[1107,15658,29],{"class":1783},[1107,15660,15453],{"class":15356},[1107,15662,15360],{"class":1779},[1107,15664,15363],{"class":1783},[1107,15666,15585],{"class":1966},[1107,15668,15363],{"class":1783},[1107,15670,8879],{"class":1779},[1107,15672,1805],{"class":1783},[15,15674,15675,15678],{},[19,15676,15677],{},"Deleting flow variables"," works the same way as global variables, use the change node's delete action or the Context Data panel.",[15,15680,15681],{},[165,15682],{"alt":15683,"dataZoomable":103,"src":15684,"title":15685},"\"Screenshot showing how to delete flow variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-deleting-flow-variable-using-change-node.png","Screenshot showing how to delete flow variable using the change node",[31,15687,15689],{"id":15688},"node-variables-node-level-isolation","Node Variables: Node-Level Isolation",[15,15691,15692],{},"Node variables (also called node context) are the most restrictive scope, they exist only within a single node. No other node can access or modify these variables, making them ideal for maintaining private state.",[15,15694,15695,15698],{},[19,15696,15697],{},"When to use node variables:"," Perfect for counters, temporary calculations, or any data that should remain private to a specific node. For example, a function node that generates unique IDs for database records can maintain a counter variable that's never exposed to other parts of your flow.",[399,15700,15702],{"id":15701},"working-with-node-variables","Working with Node Variables",[15,15704,15705],{},"Node variables are local to a Function node and cannot be read or modified by other nodes.",[15,15707,15708],{},[19,15709,15710],{},"Setting node variables:",[1756,15712,15714],{"className":1758,"code":15713,"language":1760,"meta":103,"style":103},"context.set('counter', 0);\ncontext.set('lastProcessedId', 'ABC123');\n",[924,15715,15716,15743],{"__ignoreMap":103},[1107,15717,15718,15721,15723,15725,15727,15729,15732,15734,15736,15739,15741],{"class":1765,"line":1766},[1107,15719,15720],{"class":1779},"context",[1107,15722,29],{"class":1783},[1107,15724,15357],{"class":15356},[1107,15726,15360],{"class":1779},[1107,15728,15363],{"class":1783},[1107,15730,15731],{"class":1966},"counter",[1107,15733,15363],{"class":1783},[1107,15735,1955],{"class":1783},[1107,15737,15738],{"class":1854}," 0",[1107,15740,8879],{"class":1779},[1107,15742,1805],{"class":1783},[1107,15744,15745,15747,15749,15751,15753,15755,15758,15760,15762,15764,15767,15769,15771],{"class":1765,"line":107},[1107,15746,15720],{"class":1779},[1107,15748,29],{"class":1783},[1107,15750,15357],{"class":15356},[1107,15752,15360],{"class":1779},[1107,15754,15363],{"class":1783},[1107,15756,15757],{"class":1966},"lastProcessedId",[1107,15759,15363],{"class":1783},[1107,15761,1955],{"class":1783},[1107,15763,15373],{"class":1783},[1107,15765,15766],{"class":1966},"ABC123",[1107,15768,15363],{"class":1783},[1107,15770,8879],{"class":1779},[1107,15772,1805],{"class":1783},[15,15774,15775],{},[19,15776,15777],{},"Retrieving node variables:",[1756,15779,15781],{"className":1758,"code":15780,"language":1760,"meta":103,"style":103},"let counter = context.get('counter');\ncounter++;\ncontext.set('counter', counter);\n",[924,15782,15783,15812,15819],{"__ignoreMap":103},[1107,15784,15785,15788,15791,15793,15796,15798,15800,15802,15804,15806,15808,15810],{"class":1765,"line":1766},[1107,15786,15787],{"class":1775},"let",[1107,15789,15790],{"class":1779}," counter ",[1107,15792,1784],{"class":1783},[1107,15794,15795],{"class":1779}," context",[1107,15797,29],{"class":1783},[1107,15799,15453],{"class":15356},[1107,15801,15360],{"class":1779},[1107,15803,15363],{"class":1783},[1107,15805,15731],{"class":1966},[1107,15807,15363],{"class":1783},[1107,15809,8879],{"class":1779},[1107,15811,1805],{"class":1783},[1107,15813,15814,15816],{"class":1765,"line":107},[1107,15815,15731],{"class":1779},[1107,15817,15818],{"class":1783},"++;\n",[1107,15820,15821,15823,15825,15827,15829,15831,15833,15835,15837,15840],{"class":1765,"line":294},[1107,15822,15720],{"class":1779},[1107,15824,29],{"class":1783},[1107,15826,15357],{"class":15356},[1107,15828,15360],{"class":1779},[1107,15830,15363],{"class":1783},[1107,15832,15731],{"class":1966},[1107,15834,15363],{"class":1783},[1107,15836,1955],{"class":1783},[1107,15838,15839],{"class":1779}," counter)",[1107,15841,1805],{"class":1783},[15,15843,15844,15847],{},[19,15845,15846],{},"Deleting node variables"," must be done through the Context Data sidebar panel.",[31,15849,15851],{"id":15850},"persistent-storage-with-flowfuse","Persistent Storage with FlowFuse",[15,15853,15854],{},"By default, all context variables (node, flow, and global) are stored in memory. This means they're lost whenever you restart Node-RED or redeploy your flows. For production applications, this is often unacceptable.",[15,15856,15857],{},"FlowFuse provides persistent storage that survives restarts, redeployments, and system updates. This ensures your application state remains intact across sessions.",[399,15859,15861],{"id":15860},"using-persistent-storage","Using Persistent Storage",[15,15863,15864],{},[19,15865,15866],{},"Setting persistent variables:",[15,15868,15869],{},"In the change node, select \"persistent\" from the store dropdown instead of \"memory\".",[15,15871,15872],{},[165,15873],{"alt":15337,"dataZoomable":103,"src":15874,"title":15339},"\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-change-node-persistent-store-option-for-while-setting-variable.gif",[15,15876,15877],{},"In function nodes, add \"persistent\" as a third parameter:",[1756,15879,15881],{"className":1758,"code":15880,"language":1760,"meta":103,"style":103},"global.set('userData', userData, 'persistent');\nflow.set('sessionConfig', config, 'persistent');\ncontext.set('processedCount', count, 'persistent');\n",[924,15882,15883,15918,15952],{"__ignoreMap":103},[1107,15884,15885,15887,15889,15891,15893,15895,15898,15900,15902,15905,15907,15909,15912,15914,15916],{"class":1765,"line":1766},[1107,15886,15232],{"class":1779},[1107,15888,29],{"class":1783},[1107,15890,15357],{"class":15356},[1107,15892,15360],{"class":1779},[1107,15894,15363],{"class":1783},[1107,15896,15897],{"class":1966},"userData",[1107,15899,15363],{"class":1783},[1107,15901,1955],{"class":1783},[1107,15903,15904],{"class":1779}," userData",[1107,15906,1955],{"class":1783},[1107,15908,15373],{"class":1783},[1107,15910,15911],{"class":1966},"persistent",[1107,15913,15363],{"class":1783},[1107,15915,8879],{"class":1779},[1107,15917,1805],{"class":1783},[1107,15919,15920,15922,15924,15926,15928,15930,15933,15935,15937,15940,15942,15944,15946,15948,15950],{"class":1765,"line":107},[1107,15921,15228],{"class":1779},[1107,15923,29],{"class":1783},[1107,15925,15357],{"class":15356},[1107,15927,15360],{"class":1779},[1107,15929,15363],{"class":1783},[1107,15931,15932],{"class":1966},"sessionConfig",[1107,15934,15363],{"class":1783},[1107,15936,1955],{"class":1783},[1107,15938,15939],{"class":1779}," config",[1107,15941,1955],{"class":1783},[1107,15943,15373],{"class":1783},[1107,15945,15911],{"class":1966},[1107,15947,15363],{"class":1783},[1107,15949,8879],{"class":1779},[1107,15951,1805],{"class":1783},[1107,15953,15954,15956,15958,15960,15962,15964,15967,15969,15971,15974,15976,15978,15980,15982,15984],{"class":1765,"line":294},[1107,15955,15720],{"class":1779},[1107,15957,29],{"class":1783},[1107,15959,15357],{"class":15356},[1107,15961,15360],{"class":1779},[1107,15963,15363],{"class":1783},[1107,15965,15966],{"class":1966},"processedCount",[1107,15968,15363],{"class":1783},[1107,15970,1955],{"class":1783},[1107,15972,15973],{"class":1779}," count",[1107,15975,1955],{"class":1783},[1107,15977,15373],{"class":1783},[1107,15979,15911],{"class":1966},[1107,15981,15363],{"class":1783},[1107,15983,8879],{"class":1779},[1107,15985,1805],{"class":1783},[15,15987,15988],{},[19,15989,15990],{},"Retrieving persistent variables:",[15,15992,15993],{},"In a change, inject, or switch node, ensure you're selecting from the \"persistent\" store.",[15,15995,15996],{},[165,15997],{"alt":15998,"dataZoomable":103,"src":15999,"title":16000},"\"Screenshot showing how to retrieve global variable using the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-change-node-persistent-store-option.gif","Screenshot showing how to retrieve global variable using the change node",[15,16002,15431],{},[1756,16004,16006],{"className":1758,"code":16005,"language":1760,"meta":103,"style":103},"const userData = global.get('userData', 'persistent');\nconst config = flow.get('sessionConfig', 'persistent');\nconst count = context.get('processedCount', 'persistent');\n",[924,16007,16008,16043,16078],{"__ignoreMap":103},[1107,16009,16010,16012,16015,16017,16019,16021,16023,16025,16027,16029,16031,16033,16035,16037,16039,16041],{"class":1765,"line":1766},[1107,16011,1776],{"class":1775},[1107,16013,16014],{"class":1779}," userData ",[1107,16016,1784],{"class":1783},[1107,16018,15448],{"class":1779},[1107,16020,29],{"class":1783},[1107,16022,15453],{"class":15356},[1107,16024,15360],{"class":1779},[1107,16026,15363],{"class":1783},[1107,16028,15897],{"class":1966},[1107,16030,15363],{"class":1783},[1107,16032,1955],{"class":1783},[1107,16034,15373],{"class":1783},[1107,16036,15911],{"class":1966},[1107,16038,15363],{"class":1783},[1107,16040,8879],{"class":1779},[1107,16042,1805],{"class":1783},[1107,16044,16045,16047,16050,16052,16054,16056,16058,16060,16062,16064,16066,16068,16070,16072,16074,16076],{"class":1765,"line":107},[1107,16046,1776],{"class":1775},[1107,16048,16049],{"class":1779}," config ",[1107,16051,1784],{"class":1783},[1107,16053,15629],{"class":1779},[1107,16055,29],{"class":1783},[1107,16057,15453],{"class":15356},[1107,16059,15360],{"class":1779},[1107,16061,15363],{"class":1783},[1107,16063,15932],{"class":1966},[1107,16065,15363],{"class":1783},[1107,16067,1955],{"class":1783},[1107,16069,15373],{"class":1783},[1107,16071,15911],{"class":1966},[1107,16073,15363],{"class":1783},[1107,16075,8879],{"class":1779},[1107,16077,1805],{"class":1783},[1107,16079,16080,16082,16085,16087,16089,16091,16093,16095,16097,16099,16101,16103,16105,16107,16109,16111],{"class":1765,"line":294},[1107,16081,1776],{"class":1775},[1107,16083,16084],{"class":1779}," count ",[1107,16086,1784],{"class":1783},[1107,16088,15795],{"class":1779},[1107,16090,29],{"class":1783},[1107,16092,15453],{"class":15356},[1107,16094,15360],{"class":1779},[1107,16096,15363],{"class":1783},[1107,16098,15966],{"class":1966},[1107,16100,15363],{"class":1783},[1107,16102,1955],{"class":1783},[1107,16104,15373],{"class":1783},[1107,16106,15911],{"class":1966},[1107,16108,15363],{"class":1783},[1107,16110,8879],{"class":1779},[1107,16112,1805],{"class":1783},[15,16114,16115],{},"Persistent storage allows Node-RED to retain state between restarts, crucial for historical metrics, long-running counters, dashboard application data, and any flow that must persist through a reboot without losing information.",[31,16117,16119],{"id":16118},"the-context-data-panel","The Context Data Panel",[15,16121,16122],{},"Node-RED includes a dedicated Context Data panel in the sidebar that provides visibility into all your variables. This panel is invaluable for debugging and understanding your application's state.",[15,16124,16125],{},[165,16126],{"alt":16127,"dataZoomable":103,"src":16128,"title":16129},"\"Screenshot showing Node-RED Context data tab\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-context-data-tab.png","Screenshot showing Node-RED Context data tab",[15,16131,16132],{},[19,16133,16134],{},"Features of the Context Data panel:",[39,16136,16137,16140,16143,16146,16149],{},[42,16138,16139],{},"View all node, flow, and global variables in organized sections",[42,16141,16142],{},"See when each variable was last updated",[42,16144,16145],{},"Copy variable names or values with one click",[42,16147,16148],{},"Refresh individual variables to see current values",[42,16150,16151],{},"Delete variables directly from the interface",[15,16153,16154],{},"To access this panel, open the sidebar and choose ‘Context Data’ from the dropdown menu. Use the refresh icon in each section to update the display with the latest values.",[15,16156,16157],{},[165,16158],{"alt":16159,"dataZoomable":103,"src":16160,"title":16161},"\"Screenshot showing Node-RED Context data tab options for managing variables\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-context-data-tab-options-for-varrables.png","Screenshot showing Node-RED Context data tab options for managing variables",[31,16163,16165],{"id":16164},"environment-variables-secure-configuration","Environment Variables: Secure Configuration",[15,16167,16168],{},"Environment variables serve a different purpose than context variables, they're designed for configuration data, especially sensitive information that shouldn't be hardcoded in your flows.",[15,16170,16171],{},"Node-RED supports environment variables at two levels:",[15,16173,16174,16177],{},[19,16175,16176],{},"Flow-level environment variables"," are accessible only within a specific flow. This is useful when different flows need different configurations. For example, one flow might connect to a development database while another connects to production, each using its own credentials.",[15,16179,16180,16183],{},[19,16181,16182],{},"Global-level environment variables"," are accessible across all flows in your instance. Use these for shared configuration like API keys that multiple flows need to reference.",[399,16185,16187],{"id":16186},"working-with-environment-variables","Working with Environment Variables",[15,16189,16190],{},[19,16191,16192],{},"Setting flow-level environment variables:",[271,16194,16195,16198,16201,16204],{},[42,16196,16197],{},"Double-click on the flow tab to open the edit dialog",[42,16199,16200],{},"Navigate to the \"Environment Variables\" section",[42,16202,16203],{},"Add your variables as name-value pairs",[42,16205,16206],{},"Click Done and deploy",[15,16208,16209],{},[165,16210],{"alt":16211,"dataZoomable":103,"src":16212,"title":16213},"\"Screenshot showing how to set flow level environment variables\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-setting-flow-scope-enviroment-variable.gif","Screenshot showing how to set flow level environment variables",[15,16215,16216,16217,16219],{},"For global-level environment variables, see ",[63,16218,14725],{"href":14312}," for detailed instructions.",[15,16221,16222],{},[19,16223,16224],{},"Accessing environment variables:",[15,16226,16227],{},"In change, inject, or switch nodes, select \"env variable\" and specify the name.",[15,16229,16230],{},[165,16231],{"alt":16232,"dataZoomable":103,"src":16233,"title":16234},"\"Screenshot showing how to retrieve environment variable in the change node\"","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Fvariables-in-node-red-retrieving-environment-variable-using-change-node.png","Screenshot showing how to retrieve environment variable in the change node",[15,16236,15431],{},[1756,16238,16240],{"className":1758,"code":16239,"language":1760,"meta":103,"style":103},"const apiKey = env.get('API_KEY');\nconst dbHost = env.get('DB_HOST');\n",[924,16241,16242,16271],{"__ignoreMap":103},[1107,16243,16244,16246,16249,16251,16254,16256,16258,16260,16262,16265,16267,16269],{"class":1765,"line":1766},[1107,16245,1776],{"class":1775},[1107,16247,16248],{"class":1779}," apiKey ",[1107,16250,1784],{"class":1783},[1107,16252,16253],{"class":1779}," env",[1107,16255,29],{"class":1783},[1107,16257,15453],{"class":15356},[1107,16259,15360],{"class":1779},[1107,16261,15363],{"class":1783},[1107,16263,16264],{"class":1966},"API_KEY",[1107,16266,15363],{"class":1783},[1107,16268,8879],{"class":1779},[1107,16270,1805],{"class":1783},[1107,16272,16273,16275,16278,16280,16282,16284,16286,16288,16290,16293,16295,16297],{"class":1765,"line":107},[1107,16274,1776],{"class":1775},[1107,16276,16277],{"class":1779}," dbHost ",[1107,16279,1784],{"class":1783},[1107,16281,16253],{"class":1779},[1107,16283,29],{"class":1783},[1107,16285,15453],{"class":15356},[1107,16287,15360],{"class":1779},[1107,16289,15363],{"class":1783},[1107,16291,16292],{"class":1966},"DB_HOST",[1107,16294,15363],{"class":1783},[1107,16296,8879],{"class":1779},[1107,16298,1805],{"class":1783},[15,16300,16301],{},"In template nodes:",[1756,16303,16305],{"className":1758,"code":16304,"language":1760,"meta":103,"style":103},"API Endpoint: {{env.API_ENDPOINT}}\n",[924,16306,16307],{"__ignoreMap":103},[1107,16308,16309,16312,16315,16317,16320,16323,16325,16328],{"class":1765,"line":1766},[1107,16310,16311],{"class":1779},"API ",[1107,16313,16314],{"class":10543},"Endpoint",[1107,16316,992],{"class":1783},[1107,16318,16319],{"class":1783}," {{",[1107,16321,16322],{"class":1779},"env",[1107,16324,29],{"class":1783},[1107,16326,16327],{"class":1779},"API_ENDPOINT",[1107,16329,16330],{"class":1783},"}}\n",[15,16332,16333,16334,16337],{},"For configuration nodes that don't have explicit environment variable support, you can often use the syntax ",[924,16335,16336],{},"${VARIABLE_NAME}"," in input fields.",[15,16339,16340,16343,16344,16347],{},[19,16341,16342],{},"Important note on precedence:"," When a flow-level and global-level environment variable share the same name, Node-RED uses the flow-level value. To explicitly access the global-level variable, prefix it with ",[924,16345,16346],{},"$parent."," in your reference.",[15,16349,16350,16353],{},[19,16351,16352],{},"Deleting environment variables:"," Return to the flow edit dialog where you added them and click the delete icon next to each variable. Remember to redeploy your flows after making changes.",[31,16355,15025],{"id":15024},[15,16357,16358],{},"Here are some guidelines for effective variable usage in Node-RED:",[15,16360,16361],{},[19,16362,16363],{},"Choose the right scope:",[39,16365,16366,16369,16372,16375],{},[42,16367,16368],{},"Use node variables for node-specific data",[42,16370,16371],{},"Use flow variables for data shared within a single workflow",[42,16373,16374],{},"Use global variables for system-wide shared data",[42,16376,16377],{},"Use environment variables for configuration and secrets",[15,16379,16380,16383,16384,853,16387,853,16390,1279],{},[19,16381,16382],{},"Naming conventions matter:"," Use clear, descriptive names. Consider prefixing variables with their purpose (e.g., ",[924,16385,16386],{},"sensor_temperature",[924,16388,16389],{},"config_timeout",[924,16391,16392],{},"user_preferences",[15,16394,16395,16398],{},[19,16396,16397],{},"Leverage persistent storage:"," For production applications, identify which variables need to survive restarts and use persistent storage for those.",[15,16400,16401,16404],{},[19,16402,16403],{},"Keep sensitive data in environment variables:"," Never hardcode API keys, passwords, or other secrets directly in flows. Always use environment variables.",[15,16406,16407,16410],{},[19,16408,16409],{},"Document your variables:"," Use the description field in your nodes to explain what variables are being set or read, especially for complex flows that others might maintain.",[15,16412,16413,16416],{},[19,16414,16415],{},"Monitor the Context Data panel:"," Regularly check this panel during development to verify variables are being set correctly and to catch potential issues early.",[31,16418,1425],{"id":1424},[15,16420,16421,16422,16425,16426,16429,16430,16433,16434,16436],{},"Node-RED's four variable types give you a clear toolkit for managing data and state. Use ",[19,16423,16424],{},"node context"," for private, node-specific values, ",[19,16427,16428],{},"flow variables"," for data shared within a single tab, and ",[19,16431,16432],{},"global variables"," for anything multiple flows need to reach. Reach for ",[19,16435,15236],{}," when you're handling configuration and secrets that shouldn't be hardcoded. Matching the scope to the job keeps your flows organized, predictable, and easy for others to maintain.",[15,16438,16439],{},"The one thing to keep front of mind: context variables live in memory by default, so they reset on every restart or redeploy. For anything your application genuinely depends on, counters, cached configuration, dashboard history, plan for persistent storage from the start rather than discovering the gap in production.",[15,16441,16442],{},"Get the scopes right and lean on persistence where it matters, and Node-RED will handle everything from quick prototypes to applications you can run with confidence.",[2582,16444,16445],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":103,"searchDepth":104,"depth":104,"links":16447},[16448,16449,16452,16455,16458,16461,16462,16465,16466],{"id":15262,"depth":107,"text":15263},{"id":15298,"depth":107,"text":15299,"children":16450},[16451],{"id":15311,"depth":294,"text":15312},{"id":15508,"depth":107,"text":15509,"children":16453},[16454],{"id":15521,"depth":294,"text":15522},{"id":15688,"depth":107,"text":15689,"children":16456},[16457],{"id":15701,"depth":294,"text":15702},{"id":15850,"depth":107,"text":15851,"children":16459},[16460],{"id":15860,"depth":294,"text":15861},{"id":16118,"depth":107,"text":16119},{"id":16164,"depth":107,"text":16165,"children":16463},[16464],{"id":16186,"depth":294,"text":16187},{"id":15024,"depth":107,"text":15025},{"id":1424,"depth":107,"text":1425},{"type":8392,"title":16468,"description":16469},"Take Your Node-RED Flows to Production with FlowFuse","Built by the team behind Node-RED, FlowFuse turns your flows into production-grade applications, with persistent state that survives restarts, centralized management of every instance, team collaboration with role-based access and SSO, DevOps pipelines, and built-in monitoring with crash alerts. Start your free trial today.","2024-05-06","Learn how to use Node-RED global, flow, context, and environment variables in 2026. Step-by-step examples for setting, retrieving, and persisting state, plus best practices and an FAQ.","\u002Fblog\u002F2024\u002F05\u002Fimages\u002Funderstanding-node-red-variables.png","2026-06-01",{"keywords":16475,"faq":16476,"excerpt":16501},"node-red variables, node-red global variable, node-red context variable, node-red flow variable, node-red environment variables",[16477,16480,16483,16486,16489,16492,16495,16498],{"question":16478,"answer":16479},"What are the different types of variables in Node-RED?","Node-RED has three main categories: message variables (like msg.payload) that travel with the message through nodes; context variables that store state at node, flow, or global scope; and environment variables for configuration and secrets. Context variables are further split into node, flow, and global scopes based on visibility.",{"question":16481,"answer":16482},"What is the difference between flow and global variables in Node-RED?","Flow variables are scoped to a single tab and are accessible to all nodes within that flow but isolated from other flows. Global variables are accessible across your entire Node-RED instance, making them suited for system-wide settings or data multiple flows need to share.",{"question":16484,"answer":16485},"How do I set a global variable in a Node-RED function node?","Use global.set('variableName', value) to store a value and global.get('variableName') to retrieve it. The same pattern applies to flow variables (flow.set \u002F flow.get) and node context (context.set \u002F context.get).",{"question":16487,"answer":16488},"Do Node-RED variables persist after a restart?","By default, all context variables are stored in memory and are lost on restart or redeploy. To keep them, use persistent storage by adding 'persistent' as the store option in the change node or as a third parameter in a function node, e.g. global.set('userData', userData, 'persistent'). FlowFuse provides persistent storage that survives restarts and redeployments.",{"question":16490,"answer":16491},"What is the difference between context variables and environment variables?","Context variables (node, flow, global) store runtime application state that changes as your flows run. Environment variables hold configuration data and sensitive information like API keys and credentials, kept separate from your flows for security and easier configuration management.",{"question":16493,"answer":16494},"What happens if a flow-level and global-level environment variable have the same name?","Node-RED uses the flow-level value when names collide. To explicitly access the global-level variable instead, prefix the reference with $parent. in your variable name.",{"question":16496,"answer":16497},"When should I use node context instead of flow or global variables?","Use node context for private, node-specific data such as counters or temporary calculations that no other node should access. Node variables exist only within a single function node, providing the most restrictive and isolated scope.",{"question":16499,"answer":16500},"How can I view all the variables currently set in Node-RED?","Open the Context Data panel from the sidebar dropdown. It shows all node, flow, and global variables in organized sections, lets you see when each was last updated, copy names or values, refresh individual variables, and delete them directly from the interface.",{"type":12,"value":16502},[16503],[15,16504,15210,16505,15214,16507,15217,16509,15221,16511,15225,16513,15229,16515,15233,16517,15237,16519,810,16521,853,16523,810,16525,4972,16527,810,16529,15256],{},[19,16506,15213],{},[924,16508,14911],{},[19,16510,15220],{},[19,16512,15224],{},[19,16514,15228],{},[19,16516,15232],{},[19,16518,15236],{},[924,16520,15240],{},[924,16522,15243],{},[924,16524,15246],{},[924,16526,15249],{},[924,16528,15252],{},[924,16530,15255],{},"\u002Fblog\u002F2024\u002F05\u002Funderstanding-node-flow-global-environment-variables-in-node-red",{"title":15204,"description":16471},{"loc":16531},"blog\u002F2024\u002F05\u002Funderstanding-node-flow-global-environment-variables-in-node-red","A complete guide to setting, retrieving, and persisting Node-RED variables for efficient, production-ready flows.",[8650,138,7457],"Node-RED offers four kinds of variables for managing data and state: message variables that travel with msg through nodes, and context variables in three scopes node (private to one function node), flow (shared within a tab), and global (shared across the whole instance) plus environment variables for configuration and secrets. You set and retrieve them with global.set\u002Fget, flow.set\u002Fget, and context.set\u002Fget in function nodes, or visually through the change node. Context variables live in memory by default and are lost on restart, so use persistent storage (FlowFuse provides this) for any state that must survive reboots and redeployments.","JM0J3xiIkQQRtHldz5LdXINNjSanTuLf9BICQ6fXf24",{"id":16540,"title":16541,"authors":16542,"body":16543,"cta":18068,"date":18071,"description":18072,"extension":116,"image":18073,"lastUpdated":6203,"meta":18074,"navigation":129,"path":18080,"seo":18081,"sitemap":18082,"stem":18083,"subtitle":18084,"tags":18085,"tldr":18086,"video":3,"__hash__":18087},"blog\u002Fblog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2.md","How to Build An Application With Node-RED Dashboard 2.0 (2026)",[144],{"type":12,"value":16544,"toc":18055},[16545,16548,16555,16559,16567,16575,16579,16586,16589,16593,16603,16610,16618,16625,16629,16632,16643,16819,16829,16833,16843,16850,16874,16878,16881,16904,16911,16918,17021,17034,17038,17044,17053,17060,17076,17080,17094,17111,17118,17123,17981,17991,17994,18001,18012,18015,18019,18022,18052],[15,16546,16547],{},"In this guide, we'll build a Todo application to guide you through the features and explain how you can build rich and dynamic applications too. It shows many of the features that make Dashboard 2.0 great, and why you should use it over the deprecated node-red-dashboard.",[15,16549,16550,16551,16554],{},"If you're new to Dashboard 2.0, refer to our blog post ",[63,16552,16553],{"href":3671},"Getting Started with Dashboard 2.0"," to install and get things started.",[31,16556,16558],{"id":16557},"installing-flowfuse-user-addon","Installing Flowfuse user addon",[15,16560,16561,16562,16566],{},"The FlowFuse User Addon is a plugin developed for Dashboard 2.0, that levereges the FlowFuse API to access logged in user's information at Dashboard 2.0. For detailed information refer to the ",[63,16563,16565],{"href":16564},"\u002Fblog\u002F2024\u002F04\u002Fdisplaying-logged-in-users-on-dashboard\u002F#exploring-the-flowfuse-user-addon","Exploring the FlowFuse User Addon"," and make sure to install it.",[15,16568,16569,16570,16574],{},"Before you begin the application development process, please make sure that FlowFuse user authentication is enabled. This feature adds a layer of security to your application with a login page. By combining the FlowFuse user addon with user authentication, we gain access to the logged in user's data within our application. For more information on FlowFuse user authentication, refer to the ",[63,16571,16573],{"href":16572},"\u002Fdocs\u002Fuser\u002Finstance-settings\u002F#flowfuse-user-authentication","documentation"," and make sure that it is enabled.",[31,16576,16578],{"id":16577},"building-task-management-application","Building Task Management application",[15,16580,16581],{},[165,16582],{"alt":16583,"dataZoomable":103,"src":16584,"title":16585},"\"Screenshot of the Todo application built with Node-RED Dashboard 2.0\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-task-management-system.gif","Screenshot of the task management system built with Node-RED Dashboard 2.0",[15,16587,16588],{},"Throughout this guide, we will be building a simple, secure, and personalized Task management application that will allow users to create and view their tasks.",[399,16590,16592],{"id":16591},"building-a-form-to-submit-tasks","Building a Form to Submit Tasks",[271,16594,16595,16600],{},[42,16596,1045,16597,5141],{},[19,16598,16599],{},"ui-form",[42,16601,16602],{},"Click on the edit icon next to Page 1 (The default page added when you install Dashboard 2.0) in the Dashboard 2.0 sidebar. While this step is optional, updating the page configurations as shown in the image below ensures that your application aligns with the layout described in this guide.",[15,16604,16605],{},[165,16606],{"alt":16607,"dataZoomable":103,"src":16608,"title":16609},"\"Screenshot displaying 'new task' page configurations\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-page1-configuration.png","Screenshot displaying 'new task' page configurations",[271,16611,16612],{"start":294},[42,16613,16614,16615,16617],{},"Click on the ",[19,16616,16599],{}," widget to add form elements such as title, description, due date, and priority.",[15,16619,16620],{},[165,16621],{"alt":16622,"dataZoomable":103,"src":16623,"title":16624},"\"Screenshot of ui widget form configuration\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-task-submission-form.png","Screenshot of ui widget form configuration",[399,16626,16628],{"id":16627},"storing-tasks-in-the-global-context","Storing Tasks in the Global Context",[15,16630,16631],{},"For this guide, we are storing our Tasks in Node-RED global context but storing them  in a database will make it easy to manage your task data.",[271,16633,16634,16638],{},[42,16635,1263,16636,1589],{},[19,16637,1743],{},[42,16639,16640,16641,1318],{},"Paste the below code in the ",[19,16642,1743],{},[1756,16644,16646],{"className":1758,"code":16645,"language":1760,"meta":103,"style":103},"\u002F\u002F Retrieve the existing tasks from the global context or initialize an empty array if none exists\n\nlet tasks = global.get('tasks') || [];\n\n\u002F\u002F Push the new task object into the tasks array, including the task details and the user object extracted from the message object, as each payload emitted by the Node-RED Dashboard 2.0 widgets contains user information due to the FlowFuse User Addon.\n\ntasks.push({\n  ...msg.payload,\n  ...{\n    user: msg._client.user \u002F\u002F Assign the user object to the task\n  }\n});\n\n\u002F\u002F Update the 'tasks' variable in the global context with the modified tasks array\n\nglobal.set('tasks', tasks);\n\nreturn msg;\n",[924,16647,16648,16653,16657,16692,16696,16701,16705,16718,16731,16736,16758,16763,16771,16775,16780,16784,16807,16811],{"__ignoreMap":103},[1107,16649,16650],{"class":1765,"line":1766},[1107,16651,16652],{"class":1769},"\u002F\u002F Retrieve the existing tasks from the global context or initialize an empty array if none exists\n",[1107,16654,16655],{"class":1765,"line":107},[1107,16656,1921],{"emptyLinePlaceholder":129},[1107,16658,16659,16661,16664,16666,16668,16670,16672,16674,16676,16679,16681,16684,16687,16690],{"class":1765,"line":294},[1107,16660,15787],{"class":1775},[1107,16662,16663],{"class":1779}," tasks ",[1107,16665,1784],{"class":1783},[1107,16667,15448],{"class":1779},[1107,16669,29],{"class":1783},[1107,16671,15453],{"class":15356},[1107,16673,15360],{"class":1779},[1107,16675,15363],{"class":1783},[1107,16677,16678],{"class":1966},"tasks",[1107,16680,15363],{"class":1783},[1107,16682,16683],{"class":1779},") ",[1107,16685,16686],{"class":1783},"||",[1107,16688,16689],{"class":1779}," []",[1107,16691,1805],{"class":1783},[1107,16693,16694],{"class":1765,"line":104},[1107,16695,1921],{"emptyLinePlaceholder":129},[1107,16697,16698],{"class":1765,"line":1868},[1107,16699,16700],{"class":1769},"\u002F\u002F Push the new task object into the tasks array, including the task details and the user object extracted from the message object, as each payload emitted by the Node-RED Dashboard 2.0 widgets contains user information due to the FlowFuse User Addon.\n",[1107,16702,16703],{"class":1765,"line":1896},[1107,16704,1921],{"emptyLinePlaceholder":129},[1107,16706,16707,16709,16711,16714,16716],{"class":1765,"line":1623},[1107,16708,16678],{"class":1779},[1107,16710,29],{"class":1783},[1107,16712,16713],{"class":15356},"push",[1107,16715,15360],{"class":1779},[1107,16717,9062],{"class":1783},[1107,16719,16720,16723,16725,16727,16729],{"class":1765,"line":1924},[1107,16721,16722],{"class":1783},"  ...",[1107,16724,14911],{"class":1779},[1107,16726,29],{"class":1783},[1107,16728,1792],{"class":1779},[1107,16730,9079],{"class":1783},[1107,16732,16733],{"class":1765,"line":1930},[1107,16734,16735],{"class":1783},"  ...{\n",[1107,16737,16738,16741,16743,16745,16747,16750,16752,16755],{"class":1765,"line":1940},[1107,16739,16740],{"class":1946},"    user",[1107,16742,992],{"class":1783},[1107,16744,1787],{"class":1779},[1107,16746,29],{"class":1783},[1107,16748,16749],{"class":1779},"_client",[1107,16751,29],{"class":1783},[1107,16753,16754],{"class":1779},"user ",[1107,16756,16757],{"class":1769},"\u002F\u002F Assign the user object to the task\n",[1107,16759,16760],{"class":1765,"line":1976},[1107,16761,16762],{"class":1783},"  }\n",[1107,16764,16765,16767,16769],{"class":1765,"line":2002},[1107,16766,4957],{"class":1783},[1107,16768,8879],{"class":1779},[1107,16770,1805],{"class":1783},[1107,16772,16773],{"class":1765,"line":2029},[1107,16774,1921],{"emptyLinePlaceholder":129},[1107,16776,16777],{"class":1765,"line":1691},[1107,16778,16779],{"class":1769},"\u002F\u002F Update the 'tasks' variable in the global context with the modified tasks array\n",[1107,16781,16782],{"class":1765,"line":2080},[1107,16783,1921],{"emptyLinePlaceholder":129},[1107,16785,16786,16788,16790,16792,16794,16796,16798,16800,16802,16805],{"class":1765,"line":7873},[1107,16787,15232],{"class":1779},[1107,16789,29],{"class":1783},[1107,16791,15357],{"class":15356},[1107,16793,15360],{"class":1779},[1107,16795,15363],{"class":1783},[1107,16797,16678],{"class":1966},[1107,16799,15363],{"class":1783},[1107,16801,1955],{"class":1783},[1107,16803,16804],{"class":1779}," tasks)",[1107,16806,1805],{"class":1783},[1107,16808,16809],{"class":1765,"line":7879},[1107,16810,1921],{"emptyLinePlaceholder":129},[1107,16812,16813,16815,16817],{"class":1765,"line":7885},[1107,16814,1934],{"class":1933},[1107,16816,1787],{"class":1779},[1107,16818,1805],{"class":1783},[271,16820,16821],{"start":294},[42,16822,8725,16823,16825,16826,16828],{},[19,16824,16599],{}," widget’s output to the ",[19,16827,1743],{}," node’s input.",[399,16830,16832],{"id":16831},"displaying-notification-on-successful-task-submission","Displaying notification on successful task submission",[271,16834,16835],{},[42,16836,1263,16837,16839,16840,16842],{},[19,16838,9518],{}," node onto the canvas and set ",[924,16841,1227],{}," to the confirmation message you want to display on successful task submission.",[15,16844,16845],{},[165,16846],{"alt":16847,"dataZoomable":103,"src":16848,"title":16849},"\"screenshot of the change node setting payload for notification\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-change-node-setting-payload-for-notification.png","screenshot of the change node setting payload for notification",[271,16851,16852,16861],{"start":107},[42,16853,1045,16854,16857,16858,16860],{},[19,16855,16856],{},"ui-notification"," onto the canvas select ",[19,16859,14037],{}," and set the position to \"center\".",[42,16862,8725,16863,16825,16865,16867,16868,16870,16871,16873],{},[19,16864,16599],{},[19,16866,9518],{}," node’s input and the ",[19,16869,9518],{}," node’s output to the ",[19,16872,16856],{}," widget's input.",[399,16875,16877],{"id":16876},"retrieving-and-filtering-tasks","Retrieving and Filtering Tasks",[15,16879,16880],{},"Now that we can store tasks along with the user details of who submitted them, we need to retrieve and filter them based on users, ensuring that users can only see their tasks only and not others.",[271,16882,16883,16895],{},[42,16884,1263,16885,16888,16889,16891,16892,16894],{},[19,16886,16887],{},"ui-event"," widget onto the canvas and select ",[19,16890,14037],{}," for it. The ",[19,16893,16887],{}," will enable us to display updated tasks on the table without the need for polling, as it triggers when the page reloads or changes.",[42,16896,1263,16897,16839,16899,1242,16901,29],{},[19,16898,9518],{},[924,16900,1227],{},[924,16902,16903],{},"global.tasks",[15,16905,16906],{},[165,16907],{"alt":16908,"dataZoomable":103,"src":16909,"title":16910},"\"Screenshot of the change setting retriving global context and setting to msg.payload\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-change-node-retriving-global-context-data.png","Screenshot of the change setting retriving global context and setting to msg.payload",[271,16912,16913],{"start":294},[42,16914,1263,16915,16917],{},[19,16916,1743],{}," node onto the canvas and paste the below code into it.",[1756,16919,16921],{"className":1758,"code":16920,"language":1760,"meta":103,"style":103},"\u002F\u002F Filter the payload array of tasks to include only those tasks associated with the currently logged in user.\n\nmsg.payload = msg.payload.filter((task) => task.user.userId === msg._client.user.userId);\n\n\u002F\u002F Return the modified message object containing the filtered tasks.\n\nreturn msg;\n",[924,16922,16923,16928,16932,17000,17004,17009,17013],{"__ignoreMap":103},[1107,16924,16925],{"class":1765,"line":1766},[1107,16926,16927],{"class":1769},"\u002F\u002F Filter the payload array of tasks to include only those tasks associated with the currently logged in user.\n",[1107,16929,16930],{"class":1765,"line":107},[1107,16931,1921],{"emptyLinePlaceholder":129},[1107,16933,16934,16936,16938,16941,16943,16945,16947,16949,16951,16954,16956,16958,16962,16964,16967,16970,16972,16975,16977,16980,16983,16985,16987,16989,16991,16993,16995,16998],{"class":1765,"line":294},[1107,16935,14911],{"class":1779},[1107,16937,29],{"class":1783},[1107,16939,16940],{"class":1779},"payload ",[1107,16942,1784],{"class":1783},[1107,16944,1787],{"class":1779},[1107,16946,29],{"class":1783},[1107,16948,1792],{"class":1779},[1107,16950,29],{"class":1783},[1107,16952,16953],{"class":15356},"filter",[1107,16955,15360],{"class":1779},[1107,16957,15360],{"class":1783},[1107,16959,16961],{"class":16960},"sHdIc","task",[1107,16963,8879],{"class":1783},[1107,16965,16966],{"class":1775}," =>",[1107,16968,16969],{"class":1779}," task",[1107,16971,29],{"class":1783},[1107,16973,16974],{"class":1779},"user",[1107,16976,29],{"class":1783},[1107,16978,16979],{"class":1779},"userId ",[1107,16981,16982],{"class":1783},"===",[1107,16984,1787],{"class":1779},[1107,16986,29],{"class":1783},[1107,16988,16749],{"class":1779},[1107,16990,29],{"class":1783},[1107,16992,16974],{"class":1779},[1107,16994,29],{"class":1783},[1107,16996,16997],{"class":1779},"userId)",[1107,16999,1805],{"class":1783},[1107,17001,17002],{"class":1765,"line":104},[1107,17003,1921],{"emptyLinePlaceholder":129},[1107,17005,17006],{"class":1765,"line":1868},[1107,17007,17008],{"class":1769},"\u002F\u002F Return the modified message object containing the filtered tasks.\n",[1107,17010,17011],{"class":1765,"line":1896},[1107,17012,1921],{"emptyLinePlaceholder":129},[1107,17014,17015,17017,17019],{"class":1765,"line":1623},[1107,17016,1934],{"class":1933},[1107,17018,1787],{"class":1779},[1107,17020,1805],{"class":1783},[271,17022,17023],{"start":104},[42,17024,8725,17025,16825,17027,16867,17029,17031,17032,16828],{},[19,17026,16887],{},[19,17028,9518],{},[19,17030,9518],{}," nodes’ output to the ",[19,17033,1743],{},[399,17035,17037],{"id":17036},"enabling-client-constraint-for-ui-template","Enabling client constraint for ui-template",[15,17039,17040,17041,17043],{},"Before we begin building our table to display tasks, we need to enable access to client constraints for the ",[19,17042,13863],{}," widget. Access client constraints ensure that messages or actions are specifically targeted to individual clients. For instance, if 100 people are interacting with the same task management dashboard simultaneously and one person submits a task, the notification will only be visible to that person and not to the remaining 99 individuals.",[15,17045,17046,17047,1413,17050,17052],{},"If you have experience with Node-RED Dashboard 1.0, you may recall that these client constraints were only available for ",[19,17048,17049],{},"ui-control",[19,17051,16856],{}," widgets but in Dashboard 2.0 you can enable it for any widget.",[15,17054,17055],{},[165,17056],{"alt":17057,"dataZoomable":103,"src":17058,"title":17059},"\"Screenshot displaying FF Auth tab\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-2-ff-auth-tab.png","Screenshot displaying FF Auth tab",[271,17061,17062,17065],{},[42,17063,17064],{},"Navigate to the Dashboard 2.0 sidebar and select the top-right \"FF Auth\" Tab",[42,17066,17067,17068,1413,17070,17072,17073,17075],{},"In the \"Accept Client Constraints\" option, you'll see Dashboard 2.0 widgets where this option is by default enabled for ",[19,17069,16856],{},[19,17071,17049],{},", enable it for ",[19,17074,13863],{}," as well.",[399,17077,17079],{"id":17078},"creating-a-table-and-displaying-the-task","Creating a table and displaying the task",[15,17081,17082,17083,1413,17085,17090,17091,17093],{},"In this section, we will build an interactive table using ",[19,17084,13863],{},[63,17086,17089],{"href":17087,"rel":17088},"https:\u002F\u002Fvuetifyjs.com\u002Fen\u002Fcomponents\u002Fall\u002F",[67],"vuetify component",". Vuetify offers a wide range of components, all of which are compatible with our Node-RED Dashboard 2.0's ui-template widget. You can easily use them by just simply copying and pasting them into the ",[19,17092,13863],{}," widget.",[271,17095,17096,17101],{},[42,17097,1045,17098,17100],{},[19,17099,13863],{}," widget onto the canvas",[42,17102,17103,17104,1413,17107,17110],{},"Create a new ",[19,17105,17106],{},"ui-page",[19,17108,17109],{},"ui-group"," for it. Below, I have provided a screenshot of the \"new task\" page configurations. Again You can replicate it if you want to align with the layout described in this guide, otherwise, it is optional.",[15,17112,17113],{},[165,17114],{"alt":17115,"dataZoomable":103,"src":17116,"title":17117},"\"Screenshot displaying ui-template widget with code for building table for displaying task\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-template-widget.png","Screenshot displaying ui-template widget with code for building table for displaying task",[271,17119,17120],{"start":294},[42,17121,17122],{},"Paste the below code into the widget, If you're new to Vue.js, rest assured I've included helpful comments for clarity.",[1756,17124,17127],{"className":17125,"code":17126,"language":8779,"meta":103,"style":103},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","` \u003Ctemplate>\n \u003C!-- Input field for searching tasks -->\n \u003Cv-text-field v-model=\"search\" label=\"Search\" prepend-inner-icon=\"mdi-magnify\" single-line variant=\"outlined\"\n  hide-details>\u003C\u002Fv-text-field>\n \n \u003C!-- Data table to display tasks -->\n \u003Cv-data-table :search=\"search\" :items=\"msg?.payload\">\n  \u003C!-- Custom header for the \"current\" column -->\n  \u003Ctemplate v-slot:header.current>\n   \u003Cdiv class=\"text-center\">Center-Aligned\u003C\u002Fdiv>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"priority\" column -->\n  \u003Ctemplate v-slot:item.priority=\"{ item }\">\n   \u003C!-- Display priority icon if it exists -->\n   \u003Cv-icon v-if=\"item.priority\" icon=\"mdi-alert\" color=\"red\">\u003C\u002Fv-icon>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"user\" column -->\n  \u003Ctemplate v-slot:item.user=\"{ item }\">\n   \u003C!-- Display user avatar and username -->\n   \u003Cdiv class=\"user\">\n    \u003C!-- User avatar -->\n    \u003Cimg :src=\"item.user.image\" width=\"24\" \u002F>\n    \u003C!-- Username -->\n    {% raw %}\u003Cspan>{{ item.user.username }}\u003C\u002Fspan>{% endraw %}\n   \u003C\u002Fdiv>\n  \u003C\u002Ftemplate>\n\n  \u003C!-- Template for the \"due\" column -->\n  \u003Ctemplate v-slot:item.due=\"{ item }\">\n   \u003C!-- Calculate and display the number of days between due date and current date -->\n   {% raw %}{{ daysBetween(item.due, new Date()) }} Days{% endraw %}\n  \u003C\u002Ftemplate>\n \u003C\u002Fv-data-table>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n export default {\n  data() {\n   return {\n    \u002F\u002F Search input field model\n    search: '',\n   }\n  },\n  methods: {\n   \u002F\u002F Method to calculate the number of days between two dates\n   daysBetween(date1, date2) {\n    \u002F\u002F Calculate the difference in days\n    const oneDay = 24 * 60 * 60 * 1000; \u002F\u002F hours*minutes*seconds*milliseconds\n    const firstDate = new Date(date1);\n    const secondDate = new Date(date2);\n    const diffDays = Math.round(Math.abs((firstDate - secondDate) \u002F oneDay));\n    return diffDays;\n   }\n  }\n }\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n \u002F* Styling for user avatar and username *\u002F\n .user {\n  display: flex;\n  gap: 5px; \u002F* Gap between avatar and username *\u002F\n }\n\n \u002F* Styling for user avatar *\u002F\n .user img {\n  width: 24px; \u002F* Set width of user avatar *\u002F\n }\n\u003C\u002Fstyle>\n\n",[924,17128,17129,17143,17148,17208,17220,17225,17230,17262,17267,17279,17312,17321,17325,17330,17350,17355,17404,17412,17416,17421,17440,17445,17463,17468,17502,17507,17530,17539,17547,17551,17556,17575,17580,17585,17593,17602,17610,17614,17622,17632,17642,17649,17654,17666,17671,17676,17685,17690,17709,17714,17749,17772,17794,17843,17852,17856,17860,17865,17873,17877,17888,17893,17902,17915,17930,17934,17938,17943,17954,17969,17973],{"__ignoreMap":103},[1107,17130,17131,17134,17137,17140],{"class":1765,"line":1766},[1107,17132,17133],{"class":1779},"` ",[1107,17135,17136],{"class":1783},"\u003C",[1107,17138,17139],{"class":1946},"template",[1107,17141,17142],{"class":1783},">\n",[1107,17144,17145],{"class":1765,"line":107},[1107,17146,17147],{"class":1769}," \u003C!-- Input field for searching tasks -->\n",[1107,17149,17150,17153,17156,17159,17161,17163,17166,17168,17171,17173,17175,17178,17180,17183,17185,17187,17190,17192,17195,17198,17200,17202,17205],{"class":1765,"line":294},[1107,17151,17152],{"class":1783}," \u003C",[1107,17154,17155],{"class":1946},"v-text-field",[1107,17157,17158],{"class":1775}," v-model",[1107,17160,1784],{"class":1783},[1107,17162,1970],{"class":1783},[1107,17164,17165],{"class":1966},"search",[1107,17167,1970],{"class":1783},[1107,17169,17170],{"class":1775}," label",[1107,17172,1784],{"class":1783},[1107,17174,1970],{"class":1783},[1107,17176,17177],{"class":1966},"Search",[1107,17179,1970],{"class":1783},[1107,17181,17182],{"class":1775}," prepend-inner-icon",[1107,17184,1784],{"class":1783},[1107,17186,1970],{"class":1783},[1107,17188,17189],{"class":1966},"mdi-magnify",[1107,17191,1970],{"class":1783},[1107,17193,17194],{"class":1775}," single-line",[1107,17196,17197],{"class":1775}," variant",[1107,17199,1784],{"class":1783},[1107,17201,1970],{"class":1783},[1107,17203,17204],{"class":1966},"outlined",[1107,17206,17207],{"class":1783},"\"\n",[1107,17209,17210,17213,17216,17218],{"class":1765,"line":104},[1107,17211,17212],{"class":1775},"  hide-details",[1107,17214,17215],{"class":1783},">\u003C\u002F",[1107,17217,17155],{"class":1946},[1107,17219,17142],{"class":1783},[1107,17221,17222],{"class":1765,"line":1868},[1107,17223,17224],{"class":1779}," \n",[1107,17226,17227],{"class":1765,"line":1896},[1107,17228,17229],{"class":1769}," \u003C!-- Data table to display tasks -->\n",[1107,17231,17232,17234,17237,17240,17242,17244,17246,17248,17251,17253,17255,17258,17260],{"class":1765,"line":1623},[1107,17233,17152],{"class":1783},[1107,17235,17236],{"class":1946},"v-data-table",[1107,17238,17239],{"class":1775}," :search",[1107,17241,1784],{"class":1783},[1107,17243,1970],{"class":1783},[1107,17245,17165],{"class":1966},[1107,17247,1970],{"class":1783},[1107,17249,17250],{"class":1775}," :items",[1107,17252,1784],{"class":1783},[1107,17254,1970],{"class":1783},[1107,17256,17257],{"class":1966},"msg?.payload",[1107,17259,1970],{"class":1783},[1107,17261,17142],{"class":1783},[1107,17263,17264],{"class":1765,"line":1924},[1107,17265,17266],{"class":1769},"  \u003C!-- Custom header for the \"current\" column -->\n",[1107,17268,17269,17272,17274,17277],{"class":1765,"line":1930},[1107,17270,17271],{"class":1783},"  \u003C",[1107,17273,17139],{"class":1946},[1107,17275,17276],{"class":1775}," v-slot:header.current",[1107,17278,17142],{"class":1783},[1107,17280,17281,17284,17287,17290,17292,17294,17297,17299,17302,17305,17308,17310],{"class":1765,"line":1940},[1107,17282,17283],{"class":1783},"   \u003C",[1107,17285,17286],{"class":1946},"div",[1107,17288,17289],{"class":1775}," class",[1107,17291,1784],{"class":1783},[1107,17293,1970],{"class":1783},[1107,17295,17296],{"class":1966},"text-center",[1107,17298,1970],{"class":1783},[1107,17300,17301],{"class":1783},">",[1107,17303,17304],{"class":1779},"Center-Aligned",[1107,17306,17307],{"class":1783},"\u003C\u002F",[1107,17309,17286],{"class":1946},[1107,17311,17142],{"class":1783},[1107,17313,17314,17317,17319],{"class":1765,"line":1976},[1107,17315,17316],{"class":1783},"  \u003C\u002F",[1107,17318,17139],{"class":1946},[1107,17320,17142],{"class":1783},[1107,17322,17323],{"class":1765,"line":2002},[1107,17324,1921],{"emptyLinePlaceholder":129},[1107,17326,17327],{"class":1765,"line":2029},[1107,17328,17329],{"class":1769},"  \u003C!-- Template for the \"priority\" column -->\n",[1107,17331,17332,17334,17336,17339,17341,17343,17346,17348],{"class":1765,"line":1691},[1107,17333,17271],{"class":1783},[1107,17335,17139],{"class":1946},[1107,17337,17338],{"class":1775}," v-slot:item.priority",[1107,17340,1784],{"class":1783},[1107,17342,1970],{"class":1783},[1107,17344,17345],{"class":1966},"{ item }",[1107,17347,1970],{"class":1783},[1107,17349,17142],{"class":1783},[1107,17351,17352],{"class":1765,"line":2080},[1107,17353,17354],{"class":1769},"   \u003C!-- Display priority icon if it exists -->\n",[1107,17356,17357,17359,17362,17365,17367,17369,17372,17374,17377,17379,17381,17384,17386,17389,17391,17393,17396,17398,17400,17402],{"class":1765,"line":7873},[1107,17358,17283],{"class":1783},[1107,17360,17361],{"class":1946},"v-icon",[1107,17363,17364],{"class":1775}," v-if",[1107,17366,1784],{"class":1783},[1107,17368,1970],{"class":1783},[1107,17370,17371],{"class":1966},"item.priority",[1107,17373,1970],{"class":1783},[1107,17375,17376],{"class":1775}," icon",[1107,17378,1784],{"class":1783},[1107,17380,1970],{"class":1783},[1107,17382,17383],{"class":1966},"mdi-alert",[1107,17385,1970],{"class":1783},[1107,17387,17388],{"class":1775}," color",[1107,17390,1784],{"class":1783},[1107,17392,1970],{"class":1783},[1107,17394,17395],{"class":1966},"red",[1107,17397,1970],{"class":1783},[1107,17399,17215],{"class":1783},[1107,17401,17361],{"class":1946},[1107,17403,17142],{"class":1783},[1107,17405,17406,17408,17410],{"class":1765,"line":7879},[1107,17407,17316],{"class":1783},[1107,17409,17139],{"class":1946},[1107,17411,17142],{"class":1783},[1107,17413,17414],{"class":1765,"line":7885},[1107,17415,1921],{"emptyLinePlaceholder":129},[1107,17417,17418],{"class":1765,"line":7891},[1107,17419,17420],{"class":1769},"  \u003C!-- Template for the \"user\" column -->\n",[1107,17422,17423,17425,17427,17430,17432,17434,17436,17438],{"class":1765,"line":7897},[1107,17424,17271],{"class":1783},[1107,17426,17139],{"class":1946},[1107,17428,17429],{"class":1775}," v-slot:item.user",[1107,17431,1784],{"class":1783},[1107,17433,1970],{"class":1783},[1107,17435,17345],{"class":1966},[1107,17437,1970],{"class":1783},[1107,17439,17142],{"class":1783},[1107,17441,17442],{"class":1765,"line":7902},[1107,17443,17444],{"class":1769},"   \u003C!-- Display user avatar and username -->\n",[1107,17446,17447,17449,17451,17453,17455,17457,17459,17461],{"class":1765,"line":7908},[1107,17448,17283],{"class":1783},[1107,17450,17286],{"class":1946},[1107,17452,17289],{"class":1775},[1107,17454,1784],{"class":1783},[1107,17456,1970],{"class":1783},[1107,17458,16974],{"class":1966},[1107,17460,1970],{"class":1783},[1107,17462,17142],{"class":1783},[1107,17464,17465],{"class":1765,"line":7914},[1107,17466,17467],{"class":1769},"    \u003C!-- User avatar -->\n",[1107,17469,17470,17473,17475,17478,17480,17482,17485,17487,17490,17492,17494,17497,17499],{"class":1765,"line":7919},[1107,17471,17472],{"class":1783},"    \u003C",[1107,17474,165],{"class":1946},[1107,17476,17477],{"class":1775}," :src",[1107,17479,1784],{"class":1783},[1107,17481,1970],{"class":1783},[1107,17483,17484],{"class":1966},"item.user.image",[1107,17486,1970],{"class":1783},[1107,17488,17489],{"class":1775}," width",[1107,17491,1784],{"class":1783},[1107,17493,1970],{"class":1783},[1107,17495,17496],{"class":1966},"24",[1107,17498,1970],{"class":1783},[1107,17500,17501],{"class":1783}," \u002F>\n",[1107,17503,17504],{"class":1765,"line":7925},[1107,17505,17506],{"class":1769},"    \u003C!-- Username -->\n",[1107,17508,17509,17512,17514,17516,17518,17521,17523,17525,17527],{"class":1765,"line":7931},[1107,17510,17511],{"class":1779},"    {% raw %}",[1107,17513,17136],{"class":1783},[1107,17515,1107],{"class":1946},[1107,17517,17301],{"class":1783},[1107,17519,17520],{"class":1779},"{{ item.user.username }}",[1107,17522,17307],{"class":1783},[1107,17524,1107],{"class":1946},[1107,17526,17301],{"class":1783},[1107,17528,17529],{"class":1779},"{% endraw %}\n",[1107,17531,17532,17535,17537],{"class":1765,"line":7937},[1107,17533,17534],{"class":1783},"   \u003C\u002F",[1107,17536,17286],{"class":1946},[1107,17538,17142],{"class":1783},[1107,17540,17541,17543,17545],{"class":1765,"line":7943},[1107,17542,17316],{"class":1783},[1107,17544,17139],{"class":1946},[1107,17546,17142],{"class":1783},[1107,17548,17549],{"class":1765,"line":7949},[1107,17550,1921],{"emptyLinePlaceholder":129},[1107,17552,17553],{"class":1765,"line":7955},[1107,17554,17555],{"class":1769},"  \u003C!-- Template for the \"due\" column -->\n",[1107,17557,17558,17560,17562,17565,17567,17569,17571,17573],{"class":1765,"line":7961},[1107,17559,17271],{"class":1783},[1107,17561,17139],{"class":1946},[1107,17563,17564],{"class":1775}," v-slot:item.due",[1107,17566,1784],{"class":1783},[1107,17568,1970],{"class":1783},[1107,17570,17345],{"class":1966},[1107,17572,1970],{"class":1783},[1107,17574,17142],{"class":1783},[1107,17576,17577],{"class":1765,"line":7966},[1107,17578,17579],{"class":1769},"   \u003C!-- Calculate and display the number of days between due date and current date -->\n",[1107,17581,17582],{"class":1765,"line":7972},[1107,17583,17584],{"class":1779},"   {% raw %}{{ daysBetween(item.due, new Date()) }} Days{% endraw %}\n",[1107,17586,17587,17589,17591],{"class":1765,"line":7978},[1107,17588,17316],{"class":1783},[1107,17590,17139],{"class":1946},[1107,17592,17142],{"class":1783},[1107,17594,17595,17598,17600],{"class":1765,"line":7984},[1107,17596,17597],{"class":1783}," \u003C\u002F",[1107,17599,17236],{"class":1946},[1107,17601,17142],{"class":1783},[1107,17603,17604,17606,17608],{"class":1765,"line":7990},[1107,17605,17307],{"class":1783},[1107,17607,17139],{"class":1946},[1107,17609,17142],{"class":1783},[1107,17611,17612],{"class":1765,"line":7995},[1107,17613,1921],{"emptyLinePlaceholder":129},[1107,17615,17616,17618,17620],{"class":1765,"line":8001},[1107,17617,17136],{"class":1783},[1107,17619,94],{"class":1946},[1107,17621,17142],{"class":1783},[1107,17623,17624,17627,17630],{"class":1765,"line":8007},[1107,17625,17626],{"class":1933}," export",[1107,17628,17629],{"class":1933}," default",[1107,17631,10797],{"class":1783},[1107,17633,17634,17637,17640],{"class":1765,"line":8013},[1107,17635,17636],{"class":1946},"  data",[1107,17638,17639],{"class":1783},"()",[1107,17641,10797],{"class":1783},[1107,17643,17644,17647],{"class":1765,"line":8019},[1107,17645,17646],{"class":1933},"   return",[1107,17648,10797],{"class":1783},[1107,17650,17651],{"class":1765,"line":8024},[1107,17652,17653],{"class":1769},"    \u002F\u002F Search input field model\n",[1107,17655,17656,17659,17661,17664],{"class":1765,"line":8030},[1107,17657,17658],{"class":1946},"    search",[1107,17660,992],{"class":1783},[1107,17662,17663],{"class":1783}," ''",[1107,17665,9079],{"class":1783},[1107,17667,17668],{"class":1765,"line":8036},[1107,17669,17670],{"class":1783},"   }\n",[1107,17672,17673],{"class":1765,"line":8041},[1107,17674,17675],{"class":1783},"  },\n",[1107,17677,17678,17681,17683],{"class":1765,"line":8047},[1107,17679,17680],{"class":1946},"  methods",[1107,17682,992],{"class":1783},[1107,17684,10797],{"class":1783},[1107,17686,17687],{"class":1765,"line":8053},[1107,17688,17689],{"class":1769},"   \u002F\u002F Method to calculate the number of days between two dates\n",[1107,17691,17692,17695,17697,17700,17702,17705,17707],{"class":1765,"line":8059},[1107,17693,17694],{"class":1946},"   daysBetween",[1107,17696,15360],{"class":1783},[1107,17698,17699],{"class":16960},"date1",[1107,17701,1955],{"class":1783},[1107,17703,17704],{"class":16960}," date2",[1107,17706,8879],{"class":1783},[1107,17708,10797],{"class":1783},[1107,17710,17711],{"class":1765,"line":8064},[1107,17712,17713],{"class":1769},"    \u002F\u002F Calculate the difference in days\n",[1107,17715,17716,17719,17722,17725,17728,17731,17734,17736,17738,17740,17743,17746],{"class":1765,"line":8069},[1107,17717,17718],{"class":1775},"    const",[1107,17720,17721],{"class":1779}," oneDay",[1107,17723,17724],{"class":1783}," =",[1107,17726,17727],{"class":1854}," 24",[1107,17729,17730],{"class":1783}," *",[1107,17732,17733],{"class":1854}," 60",[1107,17735,17730],{"class":1783},[1107,17737,17733],{"class":1854},[1107,17739,17730],{"class":1783},[1107,17741,17742],{"class":1854}," 1000",[1107,17744,17745],{"class":1783},";",[1107,17747,17748],{"class":1769}," \u002F\u002F hours*minutes*seconds*milliseconds\n",[1107,17750,17751,17753,17756,17758,17761,17764,17766,17768,17770],{"class":1765,"line":8075},[1107,17752,17718],{"class":1775},[1107,17754,17755],{"class":1779}," firstDate",[1107,17757,17724],{"class":1783},[1107,17759,17760],{"class":1783}," new",[1107,17762,17763],{"class":15356}," Date",[1107,17765,15360],{"class":1946},[1107,17767,17699],{"class":1779},[1107,17769,8879],{"class":1946},[1107,17771,1805],{"class":1783},[1107,17773,17774,17776,17779,17781,17783,17785,17787,17790,17792],{"class":1765,"line":8081},[1107,17775,17718],{"class":1775},[1107,17777,17778],{"class":1779}," secondDate",[1107,17780,17724],{"class":1783},[1107,17782,17760],{"class":1783},[1107,17784,17763],{"class":15356},[1107,17786,15360],{"class":1946},[1107,17788,17789],{"class":1779},"date2",[1107,17791,8879],{"class":1946},[1107,17793,1805],{"class":1783},[1107,17795,17796,17798,17801,17803,17806,17808,17811,17813,17816,17818,17821,17824,17827,17830,17832,17834,17836,17838,17841],{"class":1765,"line":8087},[1107,17797,17718],{"class":1775},[1107,17799,17800],{"class":1779}," diffDays",[1107,17802,17724],{"class":1783},[1107,17804,17805],{"class":1779}," Math",[1107,17807,29],{"class":1783},[1107,17809,17810],{"class":15356},"round",[1107,17812,15360],{"class":1946},[1107,17814,17815],{"class":1779},"Math",[1107,17817,29],{"class":1783},[1107,17819,17820],{"class":15356},"abs",[1107,17822,17823],{"class":1946},"((",[1107,17825,17826],{"class":1779},"firstDate",[1107,17828,17829],{"class":1783}," -",[1107,17831,17778],{"class":1779},[1107,17833,16683],{"class":1946},[1107,17835,810],{"class":1783},[1107,17837,17721],{"class":1779},[1107,17839,17840],{"class":1946},"))",[1107,17842,1805],{"class":1783},[1107,17844,17845,17848,17850],{"class":1765,"line":8092},[1107,17846,17847],{"class":1933},"    return",[1107,17849,17800],{"class":1779},[1107,17851,1805],{"class":1783},[1107,17853,17854],{"class":1765,"line":8098},[1107,17855,17670],{"class":1783},[1107,17857,17858],{"class":1765,"line":8104},[1107,17859,16762],{"class":1783},[1107,17861,17862],{"class":1765,"line":8109},[1107,17863,17864],{"class":1783}," }\n",[1107,17866,17867,17869,17871],{"class":1765,"line":8114},[1107,17868,17307],{"class":1783},[1107,17870,94],{"class":1946},[1107,17872,17142],{"class":1783},[1107,17874,17875],{"class":1765,"line":8119},[1107,17876,1921],{"emptyLinePlaceholder":129},[1107,17878,17879,17881,17883,17886],{"class":1765,"line":8125},[1107,17880,17136],{"class":1783},[1107,17882,2582],{"class":1946},[1107,17884,17885],{"class":1775}," scoped",[1107,17887,17142],{"class":1783},[1107,17889,17890],{"class":1765,"line":8131},[1107,17891,17892],{"class":1779}," \u002F* Styling for user avatar and username *\u002F\n",[1107,17894,17895,17898,17900],{"class":1765,"line":8137},[1107,17896,17897],{"class":1783}," .",[1107,17899,16974],{"class":10543},[1107,17901,10797],{"class":1783},[1107,17903,17904,17908,17910,17913],{"class":1765,"line":8143},[1107,17905,17907],{"class":17906},"sqsOY","  display",[1107,17909,992],{"class":1783},[1107,17911,17912],{"class":1779}," flex",[1107,17914,1805],{"class":1783},[1107,17916,17917,17920,17922,17925,17927],{"class":1765,"line":8148},[1107,17918,17919],{"class":17906},"  gap",[1107,17921,992],{"class":1783},[1107,17923,17924],{"class":1854}," 5px",[1107,17926,17745],{"class":1783},[1107,17928,17929],{"class":1769}," \u002F* Gap between avatar and username *\u002F\n",[1107,17931,17932],{"class":1765,"line":8154},[1107,17933,17864],{"class":1783},[1107,17935,17936],{"class":1765,"line":8160},[1107,17937,1921],{"emptyLinePlaceholder":129},[1107,17939,17940],{"class":1765,"line":8166},[1107,17941,17942],{"class":1779}," \u002F* Styling for user avatar *\u002F\n",[1107,17944,17945,17947,17949,17952],{"class":1765,"line":8172},[1107,17946,17897],{"class":1783},[1107,17948,16974],{"class":10543},[1107,17950,17951],{"class":10543}," img",[1107,17953,10797],{"class":1783},[1107,17955,17956,17959,17961,17964,17966],{"class":1765,"line":8177},[1107,17957,17958],{"class":17906},"  width",[1107,17960,992],{"class":1783},[1107,17962,17963],{"class":1854}," 24px",[1107,17965,17745],{"class":1783},[1107,17967,17968],{"class":1769}," \u002F* Set width of user avatar *\u002F\n",[1107,17970,17971],{"class":1765,"line":8183},[1107,17972,17864],{"class":1783},[1107,17974,17975,17977,17979],{"class":1765,"line":8189},[1107,17976,17307],{"class":1783},[1107,17978,2582],{"class":1946},[1107,17980,17142],{"class":1783},[271,17982,17983],{"start":294},[42,17984,8725,17985,17987,17988,17990],{},[19,17986,13863],{}," widget's input to the ",[19,17989,1743],{}," node's output ( function node which we have added to filter tasks based on user ).",[31,17992,17993],{"id":14998},"Deploying the flow",[15,17995,17996],{},[165,17997],{"alt":17998,"dataZoomable":103,"src":17999,"title":18000},"\"Screenshot displaying Node-RED flow of Task management system\"","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuilding-an-application-with-dashboard-task-management-application-flow.png","Screenshot displaying Node-RED flow of Task management system",[271,18002,18003,18006,18009],{},[42,18004,18005],{},"Deploy the flow by clicking the top right Deploy button.",[42,18007,18008],{},"Locate the 'Open Dashboard' button at the top-right corner of the Dashboard 2.0 sidebar and click on it to navigate to the dashboard.",[42,18010,18011],{},"Login with the registered FlowFuse account username and password.",[15,18013,18014],{},"Now, we're all set to add tasks. Navigate to the \"New Task\" page to add tasks. To view tasks, navigate to the \"your Task\" page.",[31,18016,18018],{"id":18017},"next-step","Next step",[15,18020,18021],{},"If you want to enhance this simple application by adding more features, consider the following resources:",[39,18023,18024,18031,18038,18045],{},[42,18025,18026,18030],{},[63,18027,18029],{"href":18028},"\u002Fwebinars\u002F2024\u002Fnode-red-dashboard-multi-user\u002F","Webinar"," - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.",[42,18032,18033,18037],{},[63,18034,18036],{"href":18035},"\u002Fblog\u002F2024\u002F04\u002Fdisplaying-logged-in-users-on-dashboard\u002F","Displaying logged-in users on Dashboard 2.0"," - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse.",[42,18039,18040,18044],{},[63,18041,18043],{"href":18042},"\u002Fblog\u002F2024\u002F04\u002Fbuilding-an-admin-panel-in-node-red-with-dashboard-2\u002F","How to Build an Admin Dashboard with Node-RED Dashboard 2.0"," - This detailed guide demonstrates how to build a secure admin page in Node-RED Dashboard 2.0.",[42,18046,18047,18051],{},[63,18048,18050],{"href":18049},"\u002Fblueprints\u002Fflowfuse-dashboard\u002Fmulti-user-dashboard\u002F","Multi-User Dashboard for Ticket\u002FTask Management"," blueprint, which allows you to utilize templates to develop a personalized multi-user dashboard quickly. This Task management blueprint has all features such as adding, updating, and deleting tasks, user profiles, and admin dashboard.",[2582,18053,18054],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}",{"title":103,"searchDepth":104,"depth":104,"links":18056},[18057,18058,18066,18067],{"id":16557,"depth":107,"text":16558},{"id":16577,"depth":107,"text":16578,"children":18059},[18060,18061,18062,18063,18064,18065],{"id":16591,"depth":294,"text":16592},{"id":16627,"depth":294,"text":16628},{"id":16831,"depth":294,"text":16832},{"id":16876,"depth":294,"text":16877},{"id":17036,"depth":294,"text":17037},{"id":17078,"depth":294,"text":17079},{"id":14998,"depth":107,"text":17993},{"id":18017,"depth":107,"text":18018},{"type":748,"title":18069,"description":18070},"Build and Deploy Node-RED Applications Faster","FlowFuse gives you everything you need to build, secure, and scale Node-RED applications, multi-user support, authentication, and production-ready deployment included. Start your free trial today.","2024-04-25","Learn to build custom applications effortlessly with Node-RED Dashboard 2.0. This step-by-step guide walks you through building a personalized, secure, and fully functional application.","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fbuild-application-dashboard-2.png",{"keywords":18075,"excerpt":18076},"node-red dashboard 2.0 application, build application node-red dashboard, node-red dashboard 2 tutorial, flowfuse dashboard app, node-red task management dashboard",{"type":12,"value":18077},[18078],[15,18079,16547],{},"\u002Fblog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2",{"title":16541,"description":18072},{"loc":18080},"blog\u002F2024\u002F04\u002Fhow-to-build-an-application-with-node-red-dashboard-2","A step-by-step guide to building a personalized, secure, and fully functional application with Dashboard 2.0.",[8650,138,12631,7457],"This guide walks through building a personalised, multi-user task management application with Node-RED Dashboard 2.0 and FlowFuse. It covers enabling user authentication, using the FlowFuse User Addon to access logged-in user data, storing and filtering tasks in global context per user, and rendering them in a Vuetify data table inside a ui-template widget with client constraints enabled.","uCiuBK4FAHIj8MDYEGdWktzIiWOu6ccwWYe3metOsjg",{"id":18089,"title":18090,"authors":18091,"body":18093,"cta":3,"date":18145,"description":18146,"extension":116,"image":18147,"lastUpdated":3,"meta":18148,"navigation":129,"path":18153,"seo":18154,"sitemap":18155,"stem":18156,"subtitle":18157,"tags":18158,"tldr":3,"video":3,"__hash__":18160},"blog\u002Fblog\u002F2024\u002F04\u002Fflowfuse-dedicated.md","Introducing FlowFuse Dedicated",[18092],"marian-demme",{"type":12,"value":18094,"toc":18143},[18095,18098,18101,18104,18136],[15,18096,18097],{},"Today, we are excited to officially announce FlowFuse Dedicated, a new way to use our enterprise platform as a single-tenant SaaS offering. This new offering provides all of the benefits of FlowFuse enterprise, with an added focus on data residency, isolation, and private networking to meet compliance needs.",[15,18099,18100],{},"At FlowFuse, we cater to a diverse spectrum of clients, ranging from burgeoning startups to formidable global enterprises. It's clear that a one-size-fits-all approach to deployment doesn't align with the varied needs of our customer base. FlowFuse's product already has flexible deployment options, ranging from FlowFuse Cloud, to a cloud environment of your choosing, to your own hardware on-premise. Allowing our customers to achieve compliance even in the stringent regulatory frameworks. Even sectors not traditionally bound by stringent regulatory frameworks are finding compliance to be a critical factor impacting productivity and profitability.",[15,18102,18103],{},"Here’s what sets FlowFuse Dedicated apart:",[39,18105,18106,18112,18118,18124,18130],{},[42,18107,18108,18111],{},[19,18109,18110],{},"Single-Tenant FlowFuse Platform:"," Enjoy the exclusivity of a dedicated environment, ensuring that your operations are isolated, secure, and tailored to your specific needs.",[42,18113,18114,18117],{},[19,18115,18116],{},"SaaS-like Efficiency:"," Experience the ease and simplicity of a SaaS solution, where FlowFuse takes care of the entire management and deployment process. When software is not upgraded to the latest versions, organizations use obsolete and inefficient software that can be exposed to security threats. Because FlowFuse Dedicated is fully managed by FlowFuse, customers get access to the latest software features and security updates imminently.",[42,18119,18120,18123],{},[19,18121,18122],{},"Regional Deployment Flexibility:"," FlowFuse Dedicated can be deployed in the region of your choice, ensuring data residency requirements are met while optimizing performance by reducing latency. FlowFuse Dedicated can be deployed in 30+ regions.",[42,18125,18126,18129],{},[19,18127,18128],{},"Enterprise-Grade Features:"," Enjoy access to all enterprise features and configurations as your own FlowFuse Administrator, enabling you to manage multiple teams effectively.",[42,18131,18132,18135],{},[19,18133,18134],{},"Comprehensive Compliance:"," Meet complex compliance standards with ease, thanks to full data and source code IP isolation.",[15,18137,18138,18139,29],{},"FlowFuse Dedicated represents a paradigm shift in Node-RED offerings, a solution that not only meets the demanding requirements of today's enterprises in terms of speed, efficiency, and productivity but also navigates the complex terrain of compliance and data security with unparalleled finesse. ",[63,18140,18142],{"href":2759,"rel":18141},[67],"Contact us to learn more",{"title":103,"searchDepth":104,"depth":104,"links":18144},[],"2024-04-22","FlowFuse Dedicated a new Node-RED solution with a secure, compliant, and fully managed single-tenant SaaS platform tailored for enterprise needs.","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fintroducing-flowfuse-dedicated.png",{"excerpt":18149},{"type":12,"value":18150},[18151],[15,18152,18097],{},"\u002Fblog\u002F2024\u002F04\u002Fflowfuse-dedicated",{"title":18090,"description":18146},{"loc":18153},"blog\u002F2024\u002F04\u002Fflowfuse-dedicated","The Next Level in Node-RED Solutions",[8650,138,10,18159],"dedicated","Pyw4VMwgEGIXd9BDqor7x2V_91HBPjhSUVwySs7bFs4",{"id":18162,"title":18163,"authors":18164,"body":18166,"cta":3,"date":18248,"description":18249,"extension":116,"image":18250,"lastUpdated":3,"meta":18251,"navigation":129,"path":18260,"seo":18261,"sitemap":18262,"stem":18263,"subtitle":18264,"tags":18265,"tldr":3,"video":3,"__hash__":18268},"blog\u002Fblog\u002F2024\u002F04\u002Fflowfuse-at-hannover-messe-node-red.md","FlowFuse Gears Up for Hannover Messe",[18165],"grey-dziuba",{"type":12,"value":18167,"toc":18239},[18168,18171,18176,18180,18183,18187,18190,18201,18205,18209,18220,18224,18227,18231],[15,18169,18170],{},"We're excited to announce that FlowFuse will be exhibiting at the upcoming Hannover Messe! This renowned industrial trade fair provides a platform to connect with global innovators and explore the latest advancements.  Visit us in Hall 14, Stand L59 from April 22nd - 26th.",[15,18172,18173],{},[19,18174,18175],{},"Do you need a pass?  We can help with that as well.",[31,18177,18179],{"id":18178},"understanding-node-red","Understanding Node-RED",[15,18181,18182],{},"Node-RED, created by our co-founder Nick O'Leary, is a powerful open-source tool that makes it easy to connect industrial devices, collect data, and create insightful visualizations. Its intuitive interface and wide range of integrations have made it a popular choice in manufacturing.",[31,18184,18186],{"id":18185},"scaling-with-flowfuse","Scaling with FlowFuse",[15,18188,18189],{},"As organizations adopt Node-RED more extensively, managing multiple instances can become complex. FlowFuse addresses this by providing a central platform designed to streamline Node-RED operations:",[39,18191,18192,18195,18198],{},[42,18193,18194],{},"Operations and Maintenance: Get a consolidated view of your Node-RED instances, track their health, and ensure smooth operation.",[42,18196,18197],{},"Developer Velocity: Enhance development efficiency with streamlined updates, remote access, and version control for Node-RED deployments.",[42,18199,18200],{},"Security and Compliance: Integrate Node-RED with your existing security systems, implement detailed user permissions, and maintain audit logs to satisfy industry regulations.",[31,18202,18204],{"id":18203},"experience-flowfuse-at-hannover-messe","Experience FlowFuse at Hannover Messe",[399,18206,18208],{"id":18207},"come-see-us-at-our-booth-for","Come see us at our booth for:",[39,18210,18211,18214,18217],{},[42,18212,18213],{},"Live demonstrations showcasing how FlowFuse enhances your Node-RED applications.",[42,18215,18216],{},"Discussions about tackling common challenges in industrial data management.",[42,18218,18219],{},"A chance to connect with our team and learn how we can support your goals.",[31,18221,18223],{"id":18222},"lets-connect","Let's Connect!",[15,18225,18226],{},"If you're interested in making the most of Node-RED within your manufacturing processes, FlowFuse offers the tools to simplify and secure your operations. Stop by our booth at Hannover Messe!",[31,18228,18230],{"id":18229},"get-your-free-pass-to-hannover-messe","Get Your Free Pass to Hannover Messe",[15,18232,18233,18234,18238],{},"Interested in attending Hannover Messe? Click ",[63,18235,3986],{"href":18236,"rel":18237},"https:\u002F\u002Fwww.hannovermesse.de\u002Fen\u002Fapplication\u002Fregistration\u002Fdirect-entry-tickets-passes?code=hXWhs",[67]," to claim your free pass to the event.",{"title":103,"searchDepth":104,"depth":104,"links":18240},[18241,18242,18243,18246,18247],{"id":18178,"depth":107,"text":18179},{"id":18185,"depth":107,"text":18186},{"id":18203,"depth":107,"text":18204,"children":18244},[18245],{"id":18207,"depth":294,"text":18208},{"id":18222,"depth":107,"text":18223},{"id":18229,"depth":107,"text":18230},"2024-04-11","We will be at Hannover Messe 2024 in Hall 14 at Stand L59","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fflowfuse-at-hannover-messe-node-red.png",{"excerpt":18252},{"type":12,"value":18253},[18254,18256],[15,18255,18170],{},[15,18257,18258],{},[19,18259,18175],{},"\u002Fblog\u002F2024\u002F04\u002Fflowfuse-at-hannover-messe-node-red",{"title":18163,"description":18249},{"loc":18260},"blog\u002F2024\u002F04\u002Fflowfuse-at-hannover-messe-node-red","Meet the team behind FlowFuse and contributors to Node-RED",[8650,10,138,18266,18267],"hannover messe","conference announcement","ZCu6z_JzDq2JiCgjvEtDuC2LlA_DfsAK6tyHskAdHNw",{"id":18270,"title":18271,"authors":18272,"body":18273,"cta":3,"date":18353,"description":18354,"extension":116,"image":18355,"lastUpdated":3,"meta":18356,"navigation":129,"path":18368,"seo":18369,"sitemap":18370,"stem":18371,"subtitle":18354,"tags":18372,"tldr":3,"video":3,"__hash__":18375},"blog\u002Fblog\u002F2024\u002F04\u002Fnode-red-multiplayer.md","Node-RED Multiplayer mode",[3731],{"type":12,"value":18274,"toc":18349},[18275,18289,18292,18295,18298,18301,18304,18310,18313,18320,18323,18326,18329,18333,18341],[15,18276,18277,18278,18282,18283,18288],{},"Last month I ",[63,18279,18281],{"href":18280},"\u002Fblog\u002F2024\u002F03\u002Flooking-towards-node-red-4\u002F","wrote"," about what users\ncan expect with Node-RED 4.0. One of the features I highlighted was improving the\nconcurrent editing experience. With the recent ",[63,18284,18287],{"href":18285,"rel":18286},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-4-0-0-beta-2-released\u002F87026",[67],"beta release"," of Node-RED, the first\nsteps on that journey are now available to try.",[15,18290,18291],{},"In this post, I'll cover what has been done so far and what we've got planned.",[15,18293,18294],{},"Full concurrent editing within Node-RED is not a small task, so naturally we're breaking it up into a series of iterations that each bring us a step closer to the eventual goal.",[15,18296,18297],{},"The natural first step was to add some basic awareness of who else has the editor open. On the surface, this may sound like a simple step, but it required laying lots of groundwork to achieve such as designing how the editor and runtime communicate this state in real-time.",[15,18299,18300],{},"It also meant starting to figure out the visual language we'll use in the editor to represent users. I spent time researching how other applications handle this - seeing what would work in the Node-RED context.",[15,18302,18303],{},"With Node-RED v4-beta.2, we have made these first steps available to the community. When the feature is enabled, you will now see icons in the header for all of the other users that have the editor open.",[15,18305,18306],{},[165,18307],{"alt":18308,"src":18309},"Screenshot of Node-RED with additional user icons in the header","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fnr-multiplayer.png",[18311,18312,18308],"figcaption",{},[15,18314,18315,18316,18319],{},"Clicking on their icon will show a little information box about ",[170,18317,18318],{},"where"," they are in the editor. Now, for this iteration, that information is fairly crude - it will tell you the name of the flow they have open and which node they are currently editing, but that's it. Which leads me on to what we have planned next.",[399,18321,18322],{"id":3661},"Next steps",[15,18324,18325],{},"The next iteration will be to improve how we indicate where a user is in the editor. Adding mini-icons to the editor tabs, or as annotations on individual nodes will allow us to more seamlessly indicate where a user is.",[15,18327,18328],{},"After that, one possible option will be to show the user's mouse cursor movement in real-time within the flow tab, and start to layer up the feedback we can provide as they make changes.",[399,18330,18332],{"id":18331},"trying-it-out-on-flowfuse-cloud","Trying it out on FlowFuse Cloud",[15,18334,18335,18336,29],{},"I mentioned earlier this feature has to be enabled. For a local install, details on how to do this are in the ",[63,18337,18340],{"href":18338,"rel":18339},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-4-0-0-beta-2-released\u002F87026#introducing-multiplayer-mode-2",[67],"release forum post",[15,18342,18343,18344,18348],{},"If you want to give it a go now, we already have Node-RED v4-beta.2 available to ",[63,18345,18347],{"href":3191,"rel":18346},[67],"try on FlowFuse Cloud"," - where the multiplayer feature is pre-enabled whilst it's in beta mode.",{"title":103,"searchDepth":104,"depth":104,"links":18350},[18351,18352],{"id":3661,"depth":294,"text":18322},{"id":18331,"depth":294,"text":18332},"2024-04-10","An update on our work to bring concurrent editing to Node-RED","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fnode-red-multiplayer.png",{"excerpt":18357},{"type":12,"value":18358},[18359,18366],[15,18360,18277,18361,18282,18363,18288],{},[63,18362,18281],{"href":18280},[63,18364,18287],{"href":18285,"rel":18365},[67],[15,18367,18291],{},"\u002Fblog\u002F2024\u002F04\u002Fnode-red-multiplayer",{"title":18271,"description":18354},{"loc":18368},"blog\u002F2024\u002F04\u002Fnode-red-multiplayer",[8650,10,138,18373,18374],"collaborative editing","concurrent editing","z0o2ypUwP45VxAfBiTUBqceg-CL-QG9jWg9EHLzrVUo",{"id":18377,"title":18378,"authors":18379,"body":18380,"cta":18497,"date":18500,"description":18501,"extension":116,"image":18432,"lastUpdated":3,"meta":18502,"navigation":129,"path":18511,"seo":18512,"sitemap":18513,"stem":18514,"subtitle":18515,"tags":18516,"tldr":3,"video":3,"__hash__":18521},"blog\u002Fblog\u002F2024\u002F04\u002Fnode-red-architecture.md","Node-RED Manufacturing Architecture",[18092],{"type":12,"value":18381,"toc":18489},[18382,18395,18399,18408,18413,18419,18422,18425,18428,18433,18437,18448,18454,18460,18464,18467,18471,18474,18482,18486],[15,18383,18384,18385,18389,18390,18394],{},"The architecture of a factory's Node-RED infrastructure is a common topic of discussion and inquiry. Fundamentally, my response to such queries unfolds in two parts. Initially, the focus must be on data organization. I champion a structure centered around a ",[63,18386,18388],{"href":18387},"\u002Fsolutions\u002Funs\u002F","Unified Namespace",", a concept I explore in depth in this article ",[63,18391,18393],{"href":18392},"\u002Fblog\u002F2024\u002F02\u002Fnode-red-unified-namespace-architecture\u002F","\"Node-RED in a Unified Namespace Architecture.\""," However, this is only one part of the inquiry. The other part of the question delves into the positioning of FlowFuse and Node-RED within the network infrastructure.",[31,18396,18398],{"id":18397},"understanding-the-multilayered-approach-the-foundation-of-factory-architecture","Understanding the Multilayered Approach: The Foundation of Factory Architecture",[15,18400,18401,18402,18407],{},"For illustrative purposes, I’ve opted to reference the ",[63,18403,18406],{"href":18404,"rel":18405},"https:\u002F\u002Fwebstore.ansi.org\u002Fpreview-pages\u002FISA\u002Fpreview_S_990001_2007.pdf",[67],"Purdue \u002F ISA-99 Model",", despite its fair share of criticism. Many of today's factories and manufacturing spaces adhere to this model, and the aim here is to mirror a realistic network scenario. For the sake of clarity, I've condensed the network into three primary layers.",[15,18409,18410],{},[19,18411,18412],{},"ISA-99 Visualization",[15,18414,18415],{},[165,18416],{"alt":18417,"dataZoomable":103,"src":18418},"Purdue Model","\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fpurdue-model.png",[15,18420,18421],{},"At the base is the Shopfloor layer. This is where all the physical factory equipment resides. Often, this layer is segmented further, but for our discussion, it's represented as a single zone.",[15,18423,18424],{},"Next is the Edge layer, which serves as the communicative conduit between the Shopfloor and the Enterprise layers. This layer often contains a Demilitarized Zone (DMZ) where various Gateways are positioned.",[15,18426,18427],{},"At the top, we find the Enterprise Layer. This can signify either cloud services or a company-wide accessible network. Frequently, the Cloud and Enterprise layers are divided, but in our case, it doesn’t matter whether they are separated.",[15,18429,18430],{},[165,18431],{"alt":18378,"dataZoomable":103,"src":18432},"\u002Fblog\u002F2024\u002F04\u002Fimages\u002Fnode-red-architecture.png",[31,18434,18436],{"id":18435},"applications-across-factory-layers","Applications Across Factory Layers",[15,18438,18439,18442,18443,18447],{},[19,18440,18441],{},"Shopfloor:"," Node-RED's versatility shines at the shopfloor level, where it’s typically installed on a variety of industrial devices. A prominent application is the development of simple Human-Machine Interfaces (HMIs) using the ",[63,18444,18446],{"href":3463,"rel":18445},[67],"Dashboard module",", which enables operators to interact with the machines visually and intuitively. Additionally, Node-RED is instrumental in data acquisition tasks, where it can gather, process, and interpret data from sensors and actuators in real-time. This capability allows for the monitoring of production processes, predictive maintenance, and the triggering of automated workflows in response to specific conditions or anomalies detected on the shop floor.",[15,18449,18450,18453],{},[19,18451,18452],{},"Edge:"," At the edge level, Node-RED plays a pivotal role in harmonizing the divide between Operational Technology (OT) and Information Technology (IT). Its gateway functionalities are not only integral in facilitating the flow of data between the shopfloor devices and the enterprise-level systems but also it is at this juncture that plant-wide Key Performance Indicators (KPIs) are generated. These KPIs, aggregated from various data points across the production line, are essential for operational oversight and strategic decision-making. Edge-level Node-RED instances can run local analytics and manage alarms.",[15,18455,18456,18459],{},[19,18457,18458],{},"Enterprise Network:"," Within the higher echelons of the network, FlowFuse establishes its domain, serving as a central hub for managing multiple Node-RED instances. In this space, Node-RED’s agility is leveraged for creating test environments, where simulated data flows can be used to model and understand potential changes before they are deployed on the shopfloor. For enterprise-wide KPI calculations, advanced analytics, and integration with other business systems like ERP or CRM. This layer can use data processed at the edge to inform enterprise-level decisions, drive continuous improvement, and harness machine learning models to glean deeper insights into the production process, quality control, and supply chain logistics.",[399,18461,18463],{"id":18462},"the-device-agent-empowering-remote-node-red-instances","The Device Agent: Empowering Remote Node-RED Instances",[15,18465,18466],{},"Every Node-RED instance that isn’t hosted within the Enterprise\u002FCloud Network’s domain of FlowFuse operates on a Device Agent. This simple tool runs Node-RED and facilitates the connection to FlowFuse. Such remote Node-RED instances are referred to as Devices.",[399,18468,18470],{"id":18469},"how-to-communicate-between-node-red-instance-project-nodes","How to communicate between Node-RED Instance? Project Nodes",[15,18472,18473],{},"Facilitating communication between Node-RED instances is a fundamental requirement for creating a cohesive and responsive Node-RED architecture. FlowFuse elevates this capability by providing an event-driven communication framework (based on MQTT) that binds different Node-RED instances together. This is primarily achieved through the use of Project Links, a feature within FlowFuse that allows for the smooth transfer of data between instances.",[15,18475,18476,18477,18481],{},"These ",[63,18478,18480],{"href":18479},"\u002Fdocs\u002Fuser\u002Fprojectnodes\u002F","Project Links"," are more than just communication channels; they represent a method of organizing Node-RED instances into a networked application, where each instance can be considered a node within the project. With Project Links, instances can subscribe to specific events or topics and publish messages that other instances are listening for. This is particularly useful for triggering actions across the network, like updating a dashboard in real time or controlling devices on the shopfloor based on analytics computed at the edge.",[31,18483,18485],{"id":18484},"summary","Summary",[15,18487,18488],{},"By employing a unified namespace approach and strategically positioning Node-RED and FlowFuse within the network, we enable a seamless flow of data from the shopfloor to the enterprise level. This multilayered approach not only enhances communication between operational technology and information technology but also empowers real-time monitoring, predictive maintenance, and strategic decision-making.",{"title":103,"searchDepth":104,"depth":104,"links":18490},[18491,18492,18496],{"id":18397,"depth":107,"text":18398},{"id":18435,"depth":107,"text":18436,"children":18493},[18494,18495],{"id":18462,"depth":294,"text":18463},{"id":18469,"depth":294,"text":18470},{"id":18484,"depth":107,"text":18485},{"type":318,"title":18498,"description":18499},"Architect Your Factory Network With FlowFuse","FlowFuse helps you deploy and manage Node-RED across every layer of your factory, from shopfloor devices to enterprise systems, with centralized control and full visibility across every instance.","2024-04-02","This article explores the integration of Node-RED and FlowFuse within a factory's multilayered infrastructure, highlighting the strategic organization of data and connectivity from the shopfloor to the enterprise level for improved operational efficiency.",{"excerpt":18503},{"type":12,"value":18504},[18505],[15,18506,18384,18507,18389,18509,18394],{},[63,18508,18388],{"href":18387},[63,18510,18393],{"href":18392},"\u002Fblog\u002F2024\u002F04\u002Fnode-red-architecture",{"title":18378,"description":18501},{"loc":18511},"blog\u002F2024\u002F04\u002Fnode-red-architecture","Enhancing Factory Operations with Node-RED and FlowFuse",[8650,138,10,18517,18518,18519,18520],"manufacturing","perdue model","ISA 99","unified-namespace","ATQE15EoA5xvwo-wIeNU9J4OY8sE9wx6Mll67N2LZTs",{"id":18523,"title":18524,"authors":18525,"body":18526,"cta":3,"date":18907,"description":18908,"extension":116,"image":18909,"lastUpdated":6064,"meta":18910,"navigation":129,"path":18915,"seo":18916,"sitemap":18917,"stem":18918,"subtitle":18919,"tags":18920,"tldr":3,"video":3,"__hash__":18922},"blog\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-with-node-red.md","Using Kafka with Node-RED",[144],{"type":12,"value":18527,"toc":18894},[18528,18531,18539,18543,18546,18549,18552,18556,18559,18564,18570,18575,18581,18585,18588,18597,18601,18608,18612,18624,18644,18649,18656,18668,18675,18679,18685,18692,18703,18709,18714,18718,18721,18754,18761,18768,18772,18775,18778,18803,18810,18814,18817,18850,18857,18864,18867,18869,18875,18882,18889,18891],[15,18529,18530],{},"Kafka is one of the most powerful technologies enabling seamless data communication. Many individuals are utilizing it alongside Node-RED for real-time data exchange in their IoT and IIoT applications. However, some users are encountering difficulties in obtaining assistance with Kafka-related queries.",[15,18532,18533,18534,18538],{},"During my recent visit to the Node-RED Forum, I noticed that while some Kafka-related queries have been answered nicely, others remain unanswered or have not been satisfactorily addressed, leaving users feeling stuck. To address this issue, we've created a comprehensive Kafka guide covering everything you need to know about Kafka, from installation and connection to data transmission. For newcomers to Kafka, we recommend reading our previous blog on ",[63,18535,18537],{"href":18536},"\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-in-manufacturing\u002F","how Kafka is applied in manufacturing",", where we've covered the basics and practical applications extensively.",[31,18540,18542],{"id":18541},"discussing-problem-and-potential-solution","Discussing problem and potential solution",[15,18544,18545],{},"Let's start by discussing a problem: imagine a temperature sensor network across a city. We need to centralize and analyze this data in real time for effective monitoring and visualization.",[15,18547,18548],{},"To resolve this problem we will use Kafka, Temperature sensors will feed data into Kafka through the Kafka producer. To retrieve real-time data for visualization and monitoring, we’ll be using Kafka Consumer. We will organize the data by region. The temperature data for each region will be managed in a specific Kafka topic partition.",[15,18550,18551],{},"While in this guide, we will generate simulated data using random number expression and run both producers and consumers on the same system, practical scenarios often involve distributed setups across different devices or systems.",[31,18553,18555],{"id":18554},"installing-and-running-kafka-locally","Installing and running Kafka locally",[15,18557,18558],{},"In this part, we’ll be installing Kafka locally using Docker to simplify the installation process, so make sure you’ve got Docker installed before you dive in.",[271,18560,18561],{},[42,18562,18563],{},"Pull the zookeeper image if it is not already, and run the zookeeper container.",[1756,18565,18568],{"className":18566,"code":18567,"language":15053},[15051],"docker run -p 2181:2181 zookeeper\n",[924,18569,18567],{"__ignoreMap":103},[271,18571,18572],{"start":107},[42,18573,18574],{},"Pull the Kafka image if it is not already, and run Kafka Container, expose PORT 9092.",[1756,18576,18579],{"className":18577,"code":18578,"language":15053},[15051],"docker run -p 9092:9092 `\n-e KAFKA_ZOOKEEPER_CONNECT=\u003CYour_Private_Ip>.1:2181 `\n-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT:\u002F\u002F\u003CYour_Private_Ip>:9092 `\n-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 `\nconfluentinc\u002Fcp-kafka\n",[924,18580,18578],{"__ignoreMap":103},[31,18582,18584],{"id":18583},"running-kafka-on-the-cloud","Running Kafka on the cloud",[15,18586,18587],{},"To run Kafka on the cloud, you can consider utilizing any cloud service according to your preferences. For a guide on running Kafka on a cloud platform, the procedures may differ. You can refer to the documentation provided by your preferred cloud service for detailed instructions.",[15,18589,18590,18591,18596],{},"During the writing of this tutorial, I utilized ",[63,18592,18595],{"href":18593,"rel":18594},"https:\u002F\u002Faiven.io\u002Fkafka-connect",[67],"Aiven’s cloud data platform"," which offers the option to use Kafka in the free trial. However, you are free to choose any cloud service that suits your requirements and preferences.",[31,18598,18600],{"id":18599},"installing-dashboard-20","Installing Dashboard 2.0",[15,18602,18603,18604,18607],{},"We will be installing Dashboard 2.0 to display real-time temperature data of various regions on a chart. If you are new to Dashboard 2.0, we recommend referring to ",[63,18605,18606],{"href":3671},"Getting started with Dashboard 2.0",", which covers everything from basic concepts to installation and creating your first dashboard seamlessly.",[31,18609,18611],{"id":18610},"installing-and-configuring-kafka-custom-node","Installing and configuring Kafka custom node",[271,18613,18614,18621],{},[42,18615,18616,18617,18620],{},"Install ",[924,18618,18619],{},"node-red-kafka-manager"," by the palette manager.",[42,18622,18623],{},"Before connecting to Kafka, ensure you have the following information ready and environment variables set up as discussed below in the Adding environment variable section.",[39,18625,18626,18629,18632,18635,18638,18641],{},[42,18627,18628],{},"Host: The IP address or hostname of your Kafka broker server.",[42,18630,18631],{},"Port: Kafka typically uses port 9092 by default. Ensure this aligns with your Kafka broker's configuration.",[42,18633,18634],{},"SSL Configuration (if applicable):\nCA Certificate: The Certificate Authority (CA) certificate for validating the SSL connection.",[42,18636,18637],{},"SASL (Simple Authentication and Security Layer) Mechanism: Most of the Kafka broker servers use SASL for authentication such as 'PLAIN', 'SCRAM-SHA-256,' or 'SCRAM-SHA-512.'",[42,18639,18640],{},"Username: SASL username of Kafka broker server for authentication.",[42,18642,18643],{},"Password: SASL password of Kafka broker server for authentication.",[271,18645,18646],{"start":294},[42,18647,18648],{},"Drag the Kafka Producer node onto the Canvas, click on that node, and click on the edit icon next to the broker input field to configure it.",[15,18650,18651],{},[165,18652],{"alt":18653,"src":18654,"title":18655},"\"Screenshot showing configuration of kafka\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-kafka-configuration.png","Screenshot showing configuration of kafka",[271,18657,18658,18661],{"start":104},[42,18659,18660],{},"Enable the TLS option if your Kafka broker server is using it for secure communication.",[42,18662,18663,18664,18667],{},"After enabling the TLS option click on the edit icon next to ",[924,18665,18666],{},"add new tls-config"," and upload the CA Certificate in PEM format.",[15,18669,18670],{},[165,18671],{"alt":18672,"src":18673,"title":18674},"\"Screenshot showing TLS configuration for kafka\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-tls-configuration.png","Configuring tls for kafka",[31,18676,18678],{"id":18677},"adding-environment-variables","Adding Environment variables",[15,18680,18681,18682,18684],{},"In this section, we will be setting up environment variables for Kafka configuration. If you have read our previous blog post, you may already know why we highly suggest using environment variables for every configuration. If not, please refer to our blog post on ",[63,18683,14725],{"href":14312}," for more information.",[15,18686,18687],{},[165,18688],{"alt":18689,"src":18690,"title":18691},"\"Screenshot showing adding environment variable in Node-RED\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-setting-environment-variables.png","Screenshot showing adding environment variable in Node-RED",[271,18693,18694,18697],{},[42,18695,18696],{},"Navigate to the instance's setting and then go to the environment section.",[42,18698,16614,18699,18702],{},[924,18700,18701],{},"add variable"," button and add variables ( host, port, username, and password) for the configuration data that we discussed in the above section. To leverage the ease of configuration provided by the Kafka custom node that we are utilizing, ensure to set only one variable for both host and port in the following format:",[1756,18704,18707],{"className":18705,"code":18706,"language":15053},[15051],"[{\"host\":\u003Center IP address or hostname of your Kafka broker server >,\"port\":\u003Center port on which your kafka broker server is listening>}]\n",[924,18708,18706],{"__ignoreMap":103},[271,18710,18711],{"start":294},[42,18712,18713],{},"Click on the save button and restart the instance by clicking on the top right Action button and selecting the restart option.",[31,18715,18717],{"id":18716},"creating-a-new-kafka-topic","Creating a new Kafka topic",[15,18719,18720],{},"In this section, we'll guide you through the process of creating a Kafka topic to handle temperature data from different city regions. To ensure the segregation of data for different zones within the city, we will configure the topic to create three partitions.",[271,18722,18723,18726,18734,18742,18745,18748,18751],{},[42,18724,18725],{},"Drag an inject node onto Canvas.",[42,18727,1224,18728,1242,18730,18733],{},[924,18729,1241],{},[924,18731,18732],{},"createTopics"," string.",[42,18735,1224,18736,1242,18738,18741],{},[924,18737,1227],{},[924,18739,18740],{},"[{\"topic\": \"Temperature\",\"partitions\": 3,\"replicationFactor\":1}]",", you can create as many topics as you want at a time.",[42,18743,18744],{},"Drag the Kafka admin node onto Canvas.",[42,18746,18747],{},"Connect the inject node’s output to the Kafka admin node’s input.",[42,18749,18750],{},"Deploy the flow by clicking on the top-right red deploy button.",[42,18752,18753],{},"After the Deploy, click on the inject button to create a topic.",[15,18755,18756],{},[165,18757],{"alt":18758,"src":18759,"title":18760},"Screenshot showing set payload in inject node for creating new Kafka topic\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-inject-node.png","Screenshot showing set payload in inject node for creating new Kafka topic",[15,18762,18763],{},[165,18764],{"alt":18765,"src":18766,"title":18767},"\"Screenshot showing Node-RED flow for creating new kafka topic\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-creating-topic.png","Screenshot showing Node-RED flow for creating new kafka topic",[31,18769,18771],{"id":18770},"sending-data-to-kafka-topic","Sending Data to Kafka topic",[15,18773,18774],{},"In this part, we’ll be setting up a producer to send simulated city region temperature data to Kafka. The producer will populate one of the available partitions in our temperature topic.",[15,18776,18777],{},"We’ve already set up the temperature topic with 3 partitions. We’ll use these partitions to send data from each city region across the city (downtown region, suburban region, and industrial region). The partitioning process keeps the data separated, making it easier and faster to work with and analyze.",[271,18779,18780,18783,18786,18793,18795],{},[42,18781,18782],{},"Drag a Kafka producer node onto Canvas.",[42,18784,18785],{},"Selected added Kafka configuration.",[42,18787,18788,18789,18792],{},"Click on that node and add the topic that we have created, set the key as ",[924,18790,18791],{},"downtown",", and set the partition number as 0. (The partition index starts from zero)",[42,18794,18725],{},[42,18796,1224,18797,1242,18799,18802],{},[924,18798,1227],{},[924,18800,18801],{},"$floor($random() * 100)"," as a JSON expression and set the inject node to send the payload automatically after a specific interval of time.",[15,18804,18805],{},[165,18806],{"alt":18807,"src":18808,"title":18809},"\"Screenshot showing kafka producer configuration\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-kafka-producer.png","Screenshot showing kafka producer configuration",[31,18811,18813],{"id":18812},"receiving-data-from-kafka-topic","Receiving data from Kafka topic",[15,18815,18816],{},"In this section, we will be creating consumers who will subscribe to listen to downtown region temperature data.",[271,18818,18819,18822,18824,18830,18844,18847],{},[42,18820,18821],{},"Drag the Kafka consumer node onto Canvas.",[42,18823,18785],{},[42,18825,18826,18827,18829],{},"Click on that node, add the topic that we have created, and enter partition ",[924,18828,1855],{}," from which it will read temperature data.",[42,18831,18832,18833,1242,18835,1413,18838,1242,18840,18843],{},"Add the Change node onto the Canvas and set ",[924,18834,1241],{},[924,18836,18837],{},"msg._kafka.key",[924,18839,1227],{},[924,18841,18842],{},"$number(msg.payload)"," because the Kafka custom node we are using is converting number data into a string. (consumer returns a Kafka object containing information related topic’s partition from data received, a key which we have set in the producer section to recognize data, and other information)",[42,18845,18846],{},"Add the ui-chart node on to Canvas and select the created group in which the chart will render.",[42,18848,18849],{},"Connect the Kafka consumer node’s output to change the node’s input and change the node’s output to the chart node’s input.",[15,18851,18852],{},[165,18853],{"alt":18854,"src":18855,"title":18856},"\"Screenshot showing kafka consumer configuration\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-kafka-consumer.png","Screenshot showing kafka consumer configuration",[15,18858,18859],{},[165,18860],{"alt":18861,"src":18862,"title":18863},"\"Changing payload received from Kafka consumer\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-change-node.png","Changing payload received from Kafka consumer",[15,18865,18866],{},"Repeat the same steps to create producer and consumer for the rest of the two regions suburban and industrial, ensuring to set partitions 1 and 2 for, as we have already assigned partition 0 to the first producer created.",[31,18868,17993],{"id":14998},[15,18870,18871,18872],{},"Our temperature monitoring system is now complete and ready for deployment. To initiate the deployment process, locate the red 'Deploy' button positioned in the top right corner and navigate to ",[924,18873,18874],{},"https:\u002F\u002F\u003Cyour-instance-name>.flowfuse.cloud\u002Fdashboard",[15,18876,18877],{},[165,18878],{"alt":18879,"src":18880,"title":18881},"\"Screenshot showing Node-RED flow of Real-time temperature monitoring system\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-temperature-monitoring-system-flow.png","Screenshot showing Node-RED flow of Real-time temperature monitoring system",[15,18883,18884],{},[165,18885],{"alt":18886,"src":18887,"title":18888},"\"Screenshot showing Dashboard 2.0 view of Real-time temperature monitoring system\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red-temperature-monitoring-system.gif","Screenshot showing Dashboard 2.0 view of Real-time temperature monitoring system",[31,18890,1425],{"id":1424},[15,18892,18893],{},"In this guide, we’ve gone over everything you need to know about how to get started with Kafka and Node-RED. additionally, in this article, we’re going to focus on solving a problem where the sensor data across the city need to be centrally stored for efficient monitoring and visualization. By solving this problem step-by-step, you’ll understand how to integrate Kafka into your Node-RED applications.",{"title":103,"searchDepth":104,"depth":104,"links":18895},[18896,18897,18898,18899,18900,18901,18902,18903,18904,18905,18906],{"id":18541,"depth":107,"text":18542},{"id":18554,"depth":107,"text":18555},{"id":18583,"depth":107,"text":18584},{"id":18599,"depth":107,"text":18600},{"id":18610,"depth":107,"text":18611},{"id":18677,"depth":107,"text":18678},{"id":18716,"depth":107,"text":18717},{"id":18770,"depth":107,"text":18771},{"id":18812,"depth":107,"text":18813},{"id":14998,"depth":107,"text":17993},{"id":1424,"depth":107,"text":1425},"2024-03-29","Learn how to seamlessly integrate Apache Kafka with Node-RED to facilitate real-time data communication. This step-by-step guide covers everything you need to harness the full potential of Kafka in your Node-RED applications.","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-with-node-red.png",{"excerpt":18911},{"type":12,"value":18912},[18913],[15,18914,18530],{},"\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-with-node-red",{"title":18524,"description":18908},{"loc":18915},"blog\u002F2024\u002F03\u002Fusing-kafka-with-node-red","Step-by-step Guide to Using Kafka with Node-RED for sending and receiving data.",[8650,138,18921],"kafka","VrjSy6M6G-lsO7mHKJNtLbAa9xhSrrrAqrPHtKCjOE4",{"id":18924,"title":18925,"authors":18926,"body":18927,"cta":3,"date":19159,"description":19160,"extension":116,"image":19161,"lastUpdated":3,"meta":19162,"navigation":129,"path":19167,"seo":19168,"sitemap":19169,"stem":19170,"subtitle":19160,"tags":19171,"tldr":3,"video":3,"__hash__":19174},"blog\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-in-manufacturing.md","How Kafka is applied in manufacturing",[144],{"type":12,"value":18928,"toc":19151},[18929,18932,18935,18939,18942,18949,18953,18956,18963,18968,18976,18981,18986,18991,18999,19004,19012,19017,19025,19030,19038,19042,19045,19065,19069,19072,19077,19085,19091,19097,19103,19109,19115,19119,19122,19139,19146,19148],[15,18930,18931],{},"Have you ever wondered how manufacturing and automotive industries can effectively manage the vast amount of real-time data generated by sensors and systems throughout the production process? A few years back, these industries faced major obstacles in handling the large volume of real-time data produced by sensors placed across the production line. Even today many industries continue to grapple with similar challenges. Traditional data management systems struggle to process and analyze this data in real-time, leading to inefficiencies in operational activities and decision-making. To address these challenges, various manufacturing and automobile plants have embraced technologies like Apache Kafka.",[15,18933,18934],{},"Kafka provides a distributed streaming platform that enables the efficient handling of real-time data streams. By leveraging Kafka, we can aggregate, process, and analyze data in real-time seamlessly. This guide provides a high-level overview of Kafka, covering its definition, components, functionality, applications, and limitations.",[31,18936,18938],{"id":18937},"what-is-kafka","What is Kafka?",[15,18940,18941],{},"Apache Kafka is a platform for distributed data streaming that allows for the publishing, subscribing, storing, and processing of streams of records in real-time. It is intended to handle data streams from multiple sources and to deliver them to multiple consumers. In essence, it can move large quantities of data in real-time from any source to any destination, simultaneously.",[15,18943,18944,18945,29],{},"Kafka is also a very good ",[63,18946,18948],{"href":18947},"\u002Fblog\u002F2024\u002F01\u002Funified-namespace-what-broker\u002F","broker for UNS architecture",[31,18950,18952],{"id":18951},"understanding-kafkas-architecture","Understanding Kafka's Architecture",[15,18954,18955],{},"Kafka architecture is designed to provide a scalable and fault-tolerant platform for handling real-time data streams. The architecture consists of several key components, each component serves a specific purpose in the data processing pipeline. In this section, we will take an overview of Kafka's architecture and its key components.",[15,18957,18958],{},[165,18959],{"alt":18960,"dataZoomable":103,"src":18961,"title":18962},"\"Architecture of Kafka\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-in-manufacturing-kafka-architecture.png","Architecture of Kafka",[15,18964,18965],{},[19,18966,18967],{},"1. Topics and Partitions",[39,18969,18970,18973],{},[42,18971,18972],{},"Topics: Imagine topics as folders for organizing data – they act as distinct categories. Kafka arranges information into these topics for systematic storage.",[42,18974,18975],{},"Partitions: Think of partitions as subdivisions within topics. They enable parallel processing across multiple servers, enhancing fault tolerance and throughput.",[15,18977,18978],{},[19,18979,18980],{},"2. Producers:",[39,18982,18983],{},[42,18984,18985],{},"Producers: Producers are like architects of data flow. They decide where to send records within a topic. This decision can be balanced using a round-robin or directed by a record key for specific purposes, such as maintaining order.",[15,18987,18988],{},[19,18989,18990],{},"3. Brokers:",[39,18992,18993,18996],{},[42,18994,18995],{},"Definition: Brokers are the backbone servers in a Kafka cluster.",[42,18997,18998],{},"Tasks: Brokers store data, handle requests from both producers and consumers, and maintain the integrity and persistence of data. They also manage the critical task of tracking offsets, which determine the position of consumers within partitions.",[15,19000,19001],{},[19,19002,19003],{},"4. Consumers and Consumer Groups:",[39,19005,19006,19009],{},[42,19007,19008],{},"Consumers: These entities read data from brokers. They subscribe to one or more topics and pull data from the specific partitions they are interested in.",[42,19010,19011],{},"Consumer Groups: Consumers collaborate in groups to scale data processing. Kafka dynamically assigns each consumer in a group a set of partitions from the subscribed topics, ensuring that each partition is processed by only one consumer within the group.",[15,19013,19014],{},[19,19015,19016],{},"5. Offsets",[39,19018,19019,19022],{},[42,19020,19021],{},"Definition: Offsets act as unique identifiers for records within a partition. They denote the position of a consumer in the partition.",[42,19023,19024],{},"Function: As consumers read records, they increment their offset. This allows them to resume processing from where they left off, which is crucial for handling failures or restarts. Kafka stores offset information in a specialized topic for easy recovery.",[15,19026,19027],{},[19,19028,19029],{},"6. Replication",[39,19031,19032,19035],{},[42,19033,19034],{},"Mechanism: Kafka ensures data durability by replicating partitions across multiple brokers.",[42,19036,19037],{},"Replication Factor: This configurable setting determines the number of copies of a partition in the cluster. If one broker fails, another can seamlessly take over, guaranteeing high availability.",[31,19039,19041],{"id":19040},"features-of-kafka","Features of Kafka",[15,19043,19044],{},"Now that we've gained a foundational understanding of Kafka, let's explore the key features that make it a preferred choice for many organizations. These features highlight why Kafka transcends being just another data processing tool and why it merits consideration for various use cases.",[39,19046,19047,19053,19059],{},[42,19048,19049,19052],{},[19,19050,19051],{},"High Throughput and Scalability:"," Kafka can handle thousands of messages per second and can scale horizontally and vertically to meet growing data demands without compromising performance.",[42,19054,19055,19058],{},[19,19056,19057],{},"Fault Tolerance and Reliability:"," Built to ensure reliability, Kafka guarantees fault tolerance through replication, safeguarding data against loss in the event of a broker failure. Data redundancy ensures data safety even during hardware failures.",[42,19060,19061,19064],{},[19,19062,19063],{},"Real-Time Processing and Low Latency:"," Kafka's real-time processing ensures low latency for instant data analysis, which is critical for real-time decision-making.",[31,19066,19068],{"id":19067},"applications-of-kafka","Applications of Kafka",[15,19070,19071],{},"As we explore the capabilities of Kafka, we realize that it goes beyond being just a regular data processing tool. Kafka is a strategic powerhouse that influences decision-making, operational efficiency, and overall effectiveness in various industries. In this section, we will discuss specific, practical applications of Kafka in different industries, demonstrating how its adaptability can solve unique challenges.",[15,19073,19074],{},[19,19075,19076],{},"1. Manufacturing Operations Optimization:",[39,19078,19079,19082],{},[42,19080,19081],{},"Real-time Production Monitoring: Kafka is used in manufacturing for continuous monitoring of production lines, equipment status, and inventory levels. This real-time visibility aids in optimizing production efficiency, reducing downtime, and enhancing overall supply chain management.",[42,19083,19084],{},"Quality Assurance and Yield Management: Companies utilize Kafka to monitor quality control metrics in real-time, enabling proactive measures to maintain product quality standards, minimize defects, and optimize production yield.",[15,19086,19087,19090],{},[19,19088,19089],{},"2. Predictive Maintenance:"," Organizations use Kafka to collect and analyze sensor data from machinery and equipment to predict potential failures. This helps them optimize scheduled maintenance tasks to prevent costly downtime and disruptions",[15,19092,19093,19096],{},[19,19094,19095],{},"3. Supply Chain Management:"," Kafka provides real-time visibility into supply chain operations. This enables companies to track shipments, monitor inventory levels, and coordinate with suppliers and distributors for efficient supply chain management.",[15,19098,19099,19102],{},[19,19100,19101],{},"4. Logistics and Transportation:"," Companies use Kafka to track vehicle and shipment locations in real-time, optimizing routes through the processing of streams of GPS data.",[15,19104,19105,19108],{},[19,19106,19107],{},"5. Telecommunications:"," Telecom operators utilize Kafka to monitor network performance metrics in real-time. This allows swift responses to outages or service degradations, ensuring a seamless communication network.",[15,19110,19111,19114],{},[19,19112,19113],{},"6. Financial Services:"," Banks leverage Kafka to process transactions in real-time, enabling immediate fraud detection by analyzing patterns in transaction data as they occur. This enhances overall security and compliance in financial operations.",[31,19116,19118],{"id":19117},"challenges-and-considerations","Challenges and Considerations",[15,19120,19121],{},"As beneficial as Kafka is in various industries, it also presents certain limitations and challenges that must be considered before deciding to use Kafka for your applications.",[271,19123,19124,19127,19130,19133,19136],{},[42,19125,19126],{},"Performance: Kafka both receives and transmits data. When the flow of data is compressed or decompressed, the performance is affected. For example, if the data is decompressed it will eventually drain the node memory. As a result, it affects both throughput and performance.",[42,19128,19129],{},"Complexity: As we all know Kafka is an excellent platform for streamlining messages. However, in the case of migration projects that transform data, Apache Kafka gets more complex. Hence, to interact with both data producers and consumers you need to create data pipelines.",[42,19131,19132],{},"Tool Support: There is always a concern for startup companies to use Kafka over other options.  Especially, if it remains in the long run. This is because a full set of management and monitoring tools are absent in Kafka.",[42,19134,19135],{},"Message Tweaking: Kafka uses system calls before delivering a message. Therefore, the messages are sensitive to modifications. Tweaking messages reduces the performance of Kafka to a greater extent. The performance is not impacted only under the condition of not changing the message.",[42,19137,19138],{},"Data Storage: Apache Kafka is not a recommended option for storing large sets of data. If the data is stored for a long period, the redundant copies of it are also stored. When this happens, the app must be ready to compromise its performance. For this reason, only use Kafka if there is a need to store data for a short period.",[15,19140,19141,19142,29],{},"Additionally, if you are interested in learning more about Kafka and its practical implementation, refer to our guide on Using ",[63,19143,19145],{"href":19144},"\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-with-node-red\u002F","Kafka with Node-RED",[31,19147,1425],{"id":1424},[15,19149,19150],{},"This guide provides a high-level overview of Apache Kafka, including its definition, architecture, features, and applications in various industries and the challenges or limitations of using Kafka. Kafka's versatility in real-time data processing, decision-making, and operational efficiency is highlighted, with applications ranging from manufacturing to finance. The overview aims to provide a clear understanding of Kafka's role in handling data challenges and fostering innovation across sectors.",{"title":103,"searchDepth":104,"depth":104,"links":19152},[19153,19154,19155,19156,19157,19158],{"id":18937,"depth":107,"text":18938},{"id":18951,"depth":107,"text":18952},{"id":19040,"depth":107,"text":19041},{"id":19067,"depth":107,"text":19068},{"id":19117,"depth":107,"text":19118},{"id":1424,"depth":107,"text":1425},"2024-03-15","An overview of Kafka -- How it's applied for industrial applications, and how it works","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fusing-kafka-in-manufacturing.png",{"excerpt":19163},{"type":12,"value":19164},[19165],[15,19166,18931],{},"\u002Fblog\u002F2024\u002F03\u002Fusing-kafka-in-manufacturing",{"title":18925,"description":19160},{"loc":19167},"blog\u002F2024\u002F03\u002Fusing-kafka-in-manufacturing",[8650,138,18921,19172,19173],"Manufacturing","Real-time data streaming","Yz4tdn0ryLIRh7P454BIKONszphLYkeziV5ber1tG2w",{"id":19176,"title":19177,"authors":19178,"body":19179,"cta":3,"date":19399,"description":19400,"extension":116,"image":19401,"lastUpdated":3,"meta":19402,"navigation":129,"path":19407,"seo":19408,"sitemap":19409,"stem":19410,"subtitle":19400,"tags":19411,"tldr":3,"video":3,"__hash__":19412},"blog\u002Fblog\u002F2024\u002F03\u002Flooking-towards-node-red-4.md","Looking towards Node-RED 4.0 and beyond",[3731],{"type":12,"value":19180,"toc":19387},[19181,19184,19193,19196,19199,19202,19206,19220,19226,19229,19232,19237,19241,19244,19247,19254,19274,19277,19281,19284,19293,19296,19300,19303,19306,19309,19312,19316,19323,19327,19330,19334,19337,19345,19349,19352,19355,19358,19361,19364,19367,19371,19378],[15,19182,19183],{},"With Node-RED 4.0 coming soon, I wanted to take a look at what users can expect to see with the new release,\nas well as some of the new features we're working on.",[15,19185,19186,19187,19192],{},"The Node-RED project ",[63,19188,19191],{"href":19189,"rel":19190},"https:\u002F\u002Fnodered.org\u002Fabout\u002Freleases\u002F",[67],"schedules its releases"," around a yearly major release that coincides with when a Node.js version\nreaches its end-of-support. This lets us drop support for that node.js version and update the default version of node used\nin the docker containers we publish. We treat this as a major change because  it might require actions on the end-users part to update any additional modules they have installed.",[15,19194,19195],{},"With Node-RED 4.0, we will be dropping support for anything earlier than Node 18 - with Node 20 the currently recommended version to use. That will give users almost 2 full years before needing to consider another Node.js upgrade.",[15,19197,19198],{},"Now, talking about Node.js versions is not that exciting. What is more exciting is to look at what new features are coming to Node-RED.",[15,19200,19201],{},"There are a few things already merged and ready to be released in the first beta release, and more landing each week.",[399,19203,19205],{"id":19204},"more-auto-complete","More auto-complete",[15,19207,19208,19209,19211,19212,810,19214,19216,19217,19219],{},"Node-RED already has simple auto-complete on ",[924,19210,14911],{}," fields in the editor. We've now extended that to also work with ",[924,19213,15228],{},[924,19215,15232],{}," context inputs as well as the ",[924,19218,16322],{}," type for accessing environment variables.",[15,19221,19222],{},[165,19223],{"alt":19224,"src":19225,"title":19224},"Node-RED editor autocompleting properties","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fnr4-auto-complete.png",[15,19227,19228],{},"This makes it so much easier to work with these types of properties - being sure you're using something that exists rather than having to switch between different views in the editor to get the names right.",[15,19230,19231],{},"In the case of env vars, it also shows you where the value was set - useful when you have nested groups and subflows which might be overriding a particular value.",[15,19233,175,19234,19236],{},[924,19235,14911],{}," auto-complete is still based on a built-in list of common message properties used by the core nodes. There is interest in enabling this to pull completions from 'live' messages seen by the node in question - but that's not currently in the plan for 4.0.",[399,19238,19240],{"id":19239},"timestamp-formatting","Timestamp formatting",[15,19242,19243],{},"The Inject node has provided the ability to inject a timestamp since the very early days of Node-RED. The value it actually sets is the number of milliseconds since epoch (aka January 1st, 1970). If you're used to working with JavaScript, then this is a perfectly normal way to pass times around. However, it isn't always what is needed and flows end up using a Function node to reformat it in some way.",[15,19245,19246],{},"With 4.0 we've added options to pick what format the timestamp is generated in at the start. Now, formatting times and dates can be a big can of worms of options. So, for this initial release, we've kept it simple by offering three options:",[15,19248,19249],{},[165,19250],{"alt":19251,"src":19252,"title":19253},"\"Format options for Node-RED timestamp\"","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fnr4-timestamp-formatting.png","Format options for Node-RED timestamp",[39,19255,19256,19262,19268],{},[42,19257,19258,19261],{},[170,19259,19260],{},"milliseconds since epoch"," - the existing option, just more explicitly labelled for what it is",[42,19263,19264,19267],{},[170,19265,19266],{},"YYYY-MM-DDTHH:mm:ss.sssZ"," - also known as ISO 8601",[42,19269,19270,19273],{},[170,19271,19272],{},"JavaScript Date Object"," - the standard Date object",[15,19275,19276],{},"There is scope to allow custom format strings to be set in the node - but we'll see what the feedback is on these new options first.",[399,19278,19280],{"id":19279},"a-better-csv-node","A better CSV node",[15,19282,19283],{},"The CSV node has had a big overhaul to make it more standards compliant. It turns out CSV has a whole bunch of tricky edge cases that most users don't hit - but if you did hit them you would be stuck.",[15,19285,19286,19287,19292],{},"The new node follows the ",[63,19288,19291],{"href":19289,"rel":19290},"https:\u002F\u002Fwww.ietf.org\u002Frfc\u002Frfc4180.txt",[67],"RFC4180"," standard and is also faster - wins all around.",[15,19294,19295],{},"For those flows that rely on some of the non-standard edge case behaviour of the existing node, we've kept a legacy mode in place to keep those flows working.",[399,19297,19299],{"id":19298},"customising-config-nodes-in-subflows","Customising config nodes in Subflows",[15,19301,19302],{},"This one needs a bit of explaining. Subflows are a way Node-RED lets you create a flow and add multiple reusable instances of it within your flows. For example, a subflow may connect to an MQTT broker and do some standard processing on the messages it received before sending them on. The Subflow can then expose a set of properties that can be customised for each instance. In our example, that could be the topic the MQTT node subscribes to.",[15,19304,19305],{},"However, in that example, the MQTT node's broker configuration would be locked to the same broker config node in every instance - and that's something we're solving in Node-RED 4.0.",[15,19307,19308],{},"We're making it possible to expose the choice of a configuration node in the Subflow properties - so each instance can be customised even further.",[15,19310,19311],{},"Another common use for this will be with Node-RED Dashboard - which uses config nodes to set the location of a widget. With Node-RED today, you cannot really use dashboard nodes inside subflows as you end up with multiple copies of the widgets all packed into the same group. With this update, you'll be able to configure the subflow instance with exactly what dashboard group to place its contents into.",[399,19313,19315],{"id":19314},"updated-jsonata","Updated JSONata",[15,19317,19318,19319,19322],{},"The JSONata library is used to provide the ",[924,19320,19321],{},"expression"," types in Node-RED - a really powerful way of working with JSON objects. With this release we've updated to the new major release of JSONata that comes with a bunch of performance improvements.",[399,19324,19326],{"id":19325},"and-many-more-minor-changes","And many more minor changes",[15,19328,19329],{},"I'll hold off listing them all out here, but there are plenty of other smaller changes scattered through the editor and the core nodes. Be sure to check the beta release notes when it arrives to see what else has been done.",[31,19331,19333],{"id":19332},"looking-further-ahead","Looking further ahead",[15,19335,19336],{},"Whilst all of these are great incremental improvements to Node-RED, there are some bigger items we're looking at that will really improve the overall Node-RED experience.",[15,19338,19339,19340,19344],{},"I wrote recently about improving how users can ",[63,19341,19343],{"href":19342},"\u002Fblog\u002F2024\u002F02\u002Fsoftware-development-in-node-red\u002F#testing","test their flows",". This remains something I think we really help make Node-RED stand apart from other low-code solutions. It won't be in the imminent 4.0 release, but it is definitely still on the roadmap for a future release.",[399,19346,19348],{"id":19347},"concurrent-editing","Concurrent editing",[15,19350,19351],{},"Another area we want to improve is the collaboration experience within Node-RED. Working on flows as a team is a key feature of FlowFuse, and we want to make it even easier to do.",[15,19353,19354],{},"One of the common complaints is how Node-RED currently handles multiple users editing flows at the same time. Whilst it's better than it used to be, it still makes for a very jarring experience when you have to keep merging other users' changes into your own.",[15,19356,19357],{},"Our goal is to make collaboration as simple and natural as possible.",[15,19359,19360],{},"There are a wide range of approaches we could take here. For example, a small improvement would be to merge other users' changes in the background without interrupting what you're doing. But I think we do better than that.",[15,19362,19363],{},"What if the editing experience was more like Google Docs - knowing that other users have the editor open, and being able to see their changes in real time. This would make for a truely collaborative editing experience.",[15,19365,19366],{},"There are some difficult problems to solve before we can get there, but I think this will be one of the more transformational changes to Node-RED we've had for some time.",[31,19368,19370],{"id":19369},"beta-releases","Beta releases",[15,19372,175,19373,19377],{},[63,19374,19376],{"href":19189,"rel":19375},[67],"release plan"," has Node-RED 4.0 coming around the end of April. As mentioned, we'll be doing a series of beta releases between now and then to start getting early feedback from the community.",[15,19379,19380,19381,19386],{},"Keep an eye on the ",[63,19382,19385],{"href":19383,"rel":19384},"https:\u002F\u002Fdiscourse.nodered.org\u002Fc\u002Fnews\u002F9",[67],"community forum"," for release announcements as they come.",{"title":103,"searchDepth":104,"depth":104,"links":19388},[19389,19390,19391,19392,19393,19394,19395,19398],{"id":19204,"depth":294,"text":19205},{"id":19239,"depth":294,"text":19240},{"id":19279,"depth":294,"text":19280},{"id":19298,"depth":294,"text":19299},{"id":19314,"depth":294,"text":19315},{"id":19325,"depth":294,"text":19326},{"id":19332,"depth":107,"text":19333,"children":19396},[19397],{"id":19347,"depth":294,"text":19348},{"id":19369,"depth":107,"text":19370},"2024-03-07","A look at what is coming in Node-RED 4.0","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Flooking-towards-node-red-4.png",{"excerpt":19403},{"type":12,"value":19404},[19405],[15,19406,19183],{},"\u002Fblog\u002F2024\u002F03\u002Flooking-towards-node-red-4",{"title":19177,"description":19400},{"loc":19407},"blog\u002F2024\u002F03\u002Flooking-towards-node-red-4",[8650,10,138],"3aargLgO-HH48QJxhIzhxY1LxFD8j0Yq91kS6tcI6tA",{"id":19414,"title":19415,"authors":19416,"body":19417,"cta":3,"date":19475,"description":19476,"extension":116,"image":19477,"lastUpdated":3,"meta":19478,"navigation":129,"path":19483,"seo":19484,"sitemap":19485,"stem":19486,"subtitle":19476,"tags":19487,"tldr":3,"video":3,"__hash__":19489},"blog\u002Fblog\u002F2024\u002F03\u002Finstalling-operating-node-red-behind-firewall.md","Installing and operating Node-RED behind a firewall",[2622],{"type":12,"value":19418,"toc":19470},[19419,19422,19426,19429,19442,19446,19449,19457,19460,19463,19467],[15,19420,19421],{},"Practitioners using Node-RED often find themselves in a situation where a firewall\nis deployed in their organization. This network configuration is a fact of life and is generally not controlled by the same people using Node-RED. Given security reigns supreme in Industrial IoT (IIoT), and a firewall offers a lot of benefits, we anticipate it will be deployed more often in the future, and as such it’s good to understand how you can get the most out of Node-RED when deployed behind a firewall.",[31,19423,19425],{"id":19424},"node-red-installation-with-a-firewall","Node-RED installation with a firewall",[15,19427,19428],{},"Generally, the standard install procedure for Node-RED requires a connection to the NPM servers that host the package. Due to NPM’s unaudited nature, IT is unlikely to agree to a permanent exception to the firewall to allow access to it. However, there are a couple of actions one can take to install Node-RED anyway.",[15,19430,19431,19432,19435,19436,19441],{},"First, ask for a temporary exception. Node-RED is installed in a few minutes, so if there’s a set time schedule an exception can be made there’s a regular method available again through collaboration with the network administrator. The second option is leveraging vendor specific package managers. As these are vetted repositories, it’s not uncommon that these gates in the firewall have been created and opened to you. Some vendors supply repositories for major package managers like ",[924,19433,19434],{},"apt-get"," on Debian\u002FUbuntu-based systems, or there’s a marketplace approach to install Node-RED like for example the ",[63,19437,19440],{"href":19438,"rel":19439},"https:\u002F\u002Fdeveloper.community.boschrexroth.com\u002Ft5\u002FStore-and-How-to\u002FFlowFuse-Node-RED\u002Fba-p\u002F82135",[67],"Rexroth CtrlX with Node-RED in it",". Lastly, you could consider downloading the NPM package beforehand and transferring it to your machine within the network. NPM allows the installation of local packages, which in turn allows you to create applications with Node-RED. This is generally a shadow IT action, and not recommended unless it’s approved.",[31,19443,19445],{"id":19444},"flowfuse-and-your-firewall","FlowFuse and your firewall",[15,19447,19448],{},"As FlowFuse can be installed in a VPN behind a firewall, there’s no requirement to open up a ‘gate’ in your firewall to FlowFuse servers. The safe perimeter provided remains to the outside world. The security aspect remains, though as a Node-RED developer, there are still everyday tasks you’ll need access to the outside world.",[15,19450,19451,19452,19456],{},"Consider installing third party nodes to connect your Node-RED instance to virtually any protocol or digital service. There are over 5000 of these nodes available, and as an organization it’s challenging to keep on top of. Your firewall provides one layer of security so that the data you’re accessing remains safe. FlowFuse provides a second layer of protection; we’ve introduced a ",[63,19453,19455],{"href":19454},"\u002Fcertified-nodes\u002F","“Certified Nodes” catalog",". These nodes have gone through automated and manual inspection to prevent malicious code from making it onto your production systems.",[15,19458,19459],{},"Installing these packages would typically still require you to obtain files from NPM. With FlowFuse however, a cache can be built with only vetted nodes – All other nodes remain unavailable.",[15,19461,19462],{},"Once Node-RED is installed and the initial development has been completed, FlowFuse aims to reduce the maintenance burden on both IT and OT teams too. In the same package cache aforementioned, Node-RED versions can be added. Updating Node-RED to the latest version becomes a job of just a few clicks. Updating your Node-RED instances, even behind a firewall, is imperative, as virtually all breaches are the result of daisy-chaining multiple security vulnerabilities into a high to critical event.",[31,19464,19466],{"id":19465},"wrap-up","Wrap up",[15,19468,19469],{},"FlowFuse founding engineers have decades of experience running Node-RED wherever it is valuable. Our product is the culmination of that, and we’re excited to help you become successful in your digitalization efforts – even when a firewall is in play.",{"title":103,"searchDepth":104,"depth":104,"links":19471},[19472,19473,19474],{"id":19424,"depth":107,"text":19425},{"id":19444,"depth":107,"text":19445},{"id":19465,"depth":107,"text":19466},"2024-03-06","FlowFuse was built to empower Node-RED to run everywhere, even behind a firewall","\u002Fblog\u002F2024\u002F03\u002Fimages\u002Fnode-red-vpn.png",{"excerpt":19479},{"type":12,"value":19480},[19481],[15,19482,19421],{},"\u002Fblog\u002F2024\u002F03\u002Finstalling-operating-node-red-behind-firewall",{"title":19415,"description":19476},{"loc":19483},"blog\u002F2024\u002F03\u002Finstalling-operating-node-red-behind-firewall",[8650,10,138,19488],"vpn","5Ekr9egPmAJdTozQtLKQ4cJtEjdBHIXON618Y3u3aAM",{"id":19491,"title":19492,"authors":19493,"body":19495,"cta":3,"date":19725,"description":19726,"extension":116,"image":19727,"lastUpdated":3,"meta":19728,"navigation":129,"path":19733,"seo":19734,"sitemap":19735,"stem":19736,"subtitle":19737,"tags":19738,"tldr":3,"video":3,"__hash__":19739},"blog\u002Fblog\u002F2024\u002F02\u002Ftaking-it-further-with-node-red.md","Storing Data: Getting Started with Node-RED",[19494],"rob-marcer",{"type":12,"value":19496,"toc":19718},[19497,19500,19503,19507,19510,19515,19518,19521,19532,19535,19539,19542,19545,19551,19554,19560,19562,19580,19584,19587,19590,19593,19599,19601,19646,19649,19653,19662,19665,19671,19674,19676,19704,19706,19709],[15,19498,19499],{},"It's quite straightforward to pass plenty of useful data with each message (msg) in your flows. Not only can you store information in msg.payload, but you can also place information in any other named object, for instance, msg.store.",[15,19501,19502],{},"In this article, we will explore some of the better solutions for storing and retrieving transactional information in your flows.",[1165,19504,19506],{"id":19505},"storing-data-outside-of-msgpayload","Storing data outside of msg.payload",[15,19508,19509],{},"In this example, we have data in msg.payload as well as in msg.later.",[15,19511,19512],{},[165,19513],{"alt":19506,"src":19514,"title":19506},"\u002Fblog\u002F2024\u002F02\u002Fimages\u002Fdata-outside-msg-payload.gif",[15,19516,19517],{},"If you want your debug to display the full content of the message, change the output to 'complete message object' as shown above.",[15,19519,19520],{},"You can import the flow using this code.",[15,19522,4917,19523,1150],{},[1107,19524,19525,19526,19529,19530,4957],{},"{\"id\":\"aaa1e17e5f158004\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Inject the message\",\"props\":",[1107,19527,19528],{},"{\"p\":\"payload\"},{\"p\":\"later\",\"v\":\"A string I want to be able to use later in my flow\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"Hello World\",\"payloadType\":\"str\",\"x\":690,\"y\":140,\"wires\":[[\"bce1bf09736125b7\"]]},{\"id\":\"bce1bf09736125b7\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":880,\"y\":140,\"wires\":",[1107,19531],{},[15,19533,19534],{},"Storing data outside of msg.payload can be very useful when you need access to that data later in your flow. You may notice that many nodes overwrite the content of msg.payload, so putting your data elsewhere is essential otherwise, it will be overwritten and lost.",[1165,19536,19538],{"id":19537},"tidying-your-messages","Tidying your messages",[15,19540,19541],{},"You may also want to remove data you don't need from your messages to optimize the speed of your flows.",[15,19543,19544],{},"It's easy enough to remove data you don't need, wherever it sits within your messages using the Change Node. In this example, we are going to delete the content of msg.other while leaving the rest of the message to be passed to the next Node.",[15,19546,19547],{},[165,19548],{"alt":19549,"src":19550,"title":19549},"Deleting data from msg.other","\u002Fblog\u002F2024\u002F02\u002Fimages\u002Fdelete-other.gif",[15,19552,19553],{},"The Change Node is configured as follows.",[15,19555,19556],{},[165,19557],{"alt":19558,"src":19559,"title":19558},"Change Node configuration","\u002Fblog\u002F2024\u002F02\u002Fimages\u002Fdelete.png",[15,19561,19520],{},[15,19563,4917,19564,1150],{},[1107,19565,19566,19567,19570,19571,19573,19574,19577,19578,4957],{},"{\"id\":\"1ac51e71153f7c1f\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Inject the message\",\"props\":",[1107,19568,19569],{},"{\"p\":\"payload\"},{\"p\":\"other\",\"v\":\"We don't need this string anymore\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"Hello World\",\"payloadType\":\"str\",\"x\":530,\"y\":100,\"wires\":[[\"5d3a978ad9eab443\",\"cd7609101328caa2\"]]},{\"id\":\"5d3a978ad9eab443\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":700,\"y\":60,\"wires\":",[1107,19572],{},"},{\"id\":\"cd7609101328caa2\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":",[1107,19575,19576],{},"{\"t\":\"delete\",\"p\":\"other\",\"pt\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":730,\"y\":100,\"wires\":[[\"be2f7f68dee570be\"]]},{\"id\":\"be2f7f68dee570be\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":900,\"y\":100,\"wires\":",[1107,19579],{},[1165,19581,19583],{"id":19582},"storing-data-outside-of-msgpayload-so-you-can-access-it-later-in-your-flows","Storing data outside of msg.payload so you can access it later in your flows",[15,19585,19586],{},"Storing data outside of msg.payload allows you to access it later in your flows. In this example, we inject geographical coordinates and use an API to get the sunset time for each location. We can then output the result as a sentence.",[15,19588,19589],{},"As the HTTP Node, which we are using to interact with the weather API, overwrites msg.payload with the response, we will store the submitted city name and coordinates in msg.store for later use.",[15,19591,19592],{},"You can see the flow working below.",[15,19594,19595],{},[165,19596],{"alt":19597,"src":19598,"title":19597},"Example flow which gets the sunset time for a given location","\u002Fblog\u002F2024\u002F02\u002Fimages\u002Fsunset-example.gif",[15,19600,19520],{},[15,19602,4917,19603,1150],{},[1107,19604,19605,19606,19609,19610,19613,19614,19616,19617,19620,19621,19626,19627,19629,19630,19632,19633,19635,19636,19639,19640,19643,19644,4957],{},"{\"id\":\"809cc8f4678767b7\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"London\",\"props\":",[1107,19607,19608],{},"{\"p\":\"payload.city\",\"v\":\"London\",\"vt\":\"str\"},{\"p\":\"payload.lat\",\"v\":\"51.5072\",\"vt\":\"str\"},{\"p\":\"payload.lng\",\"v\":\"0.1276\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":170,\"y\":80,\"wires\":[[\"6b5a6a2ef7a64f1a\"]]},{\"id\":\"b86d2d558eebbd7e\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Washington DC\",\"props\":",[1107,19611,19612],{},"{\"p\":\"payload.city\",\"v\":\"Washington DC\",\"vt\":\"str\"},{\"p\":\"payload.lat\",\"v\":\"38.9072\",\"vt\":\"str\"},{\"p\":\"payload.lng\",\"v\":\"77.0369\",\"vt\":\"str\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":140,\"y\":160,\"wires\":[[\"6b5a6a2ef7a64f1a\"]]},{\"id\":\"aaecc81a2de233d6\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 4\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":820,\"y\":160,\"wires\":",[1107,19615],{},"},{\"id\":\"6b5a6a2ef7a64f1a\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":",[1107,19618,19619],{},"{\"t\":\"set\",\"p\":\"store\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":320,\"y\":120,\"wires\":[[\"273ee743f8d1a5f1\",\"af43cea5866e11f5\"]]},{\"id\":\"273ee743f8d1a5f1\",\"type\":\"template\",\"z\":\"67746003c844dbc4\",\"name\":\"create the URL\",\"field\":\"url\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":{% raw %}\"",[63,19622,19625],{"href":19623,"rel":19624},"https:\u002F\u002Fapi.sunrisesunset.io\u002Fjson?lat=%7B%7Bstore.lat%7D%7D&lng=%7B%7Bstore.lng%7D%7D%22%7B%25",[67],"https:\u002F\u002Fapi.sunrisesunset.io\u002Fjson?lat={{store.lat}}&lng={{store.lng}}\"{%"," endraw %},\"output\":\"str\",\"x\":500,\"y\":120,\"wires\":[[\"52e8913233f379eb\",\"1d94053c790791ee\"]]},{\"id\":\"af43cea5866e11f5\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 5\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":480,\"y\":160,\"wires\":",[1107,19628],{},"},{\"id\":\"52e8913233f379eb\",\"type\":\"http request\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"obj\",\"paytoqs\":\"ignore\",\"url\":\"\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":",[1107,19631],{},",\"x\":670,\"y\":120,\"wires\":[[\"aaecc81a2de233d6\",\"a245b322ade3a7e9\"]]},{\"id\":\"1d94053c790791ee\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 6\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":660,\"y\":80,\"wires\":",[1107,19634],{},"},{\"id\":\"a245b322ade3a7e9\",\"type\":\"template\",\"z\":\"67746003c844dbc4\",\"name\":\"Create the sentence\",\"field\":\"payload\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"The sun will set at ",[2526,19637],{"value":19638},"payload.results.sunset"," in ",[2526,19641],{"value":19642},"store.city",".\",\"output\":\"str\",\"x\":860,\"y\":120,\"wires\":[[\"3e4b2d3644845f91\"]]},{\"id\":\"3e4b2d3644845f91\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 7\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1040,\"y\":120,\"wires\":",[1107,19645],{},[15,19647,19648],{},"Using this technique, we can build the content of message.store (or any other name you'd like to use) and then output the content at the end of a flow, even if you use msg.payload to interact with APIs and other custom nodes.",[1165,19650,19652],{"id":19651},"why-not-store-the-values-in-context","Why not store the values in context?",[15,19654,19655,19656,19661],{},"Where possible, it's more robust to store all the information related to a particular message within the message rather than saving it to context and retrieving it later. Using context risks a ",[63,19657,19660],{"href":19658,"rel":19659},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FRace_condition#:~:text=A%20race%20condition%20or%20race,to%20unexpected%20or%20inconsistent%20results",[67],"race condition"," within your flow that could result in data corruption.",[15,19663,19664],{},"In this example, we simulate how a race condition can make context a bad choice for transactional data storage. The flow passes in the name and age of two people then moves the age to context. The flow then adds a random delay for each message so that, in some cases, the messages do not reach the debug in the order they were created. After the delay, the age is pulled back from context and added to each msg.payload.",[15,19666,19667],{},[165,19668],{"alt":19669,"src":19670,"title":19669},"Example of how a race condition can make context a bad place to cache data","\u002Fblog\u002F2024\u002F02\u002Fimages\u002Frace-condition.gif",[15,19672,19673],{},"If there is a race condition in play, we should intermittently see Rob and John's stored ages being assigned to the wrong person. We can see in the image above that Rob is showing the incorrect age.",[15,19675,19520],{},[15,19677,4917,19678,1150],{},[1107,19679,19680,19681,19684,19685,19688,19689,19692,19693,19696,19697,19699,19700,19703],{},"{\"id\":\"7ac4e7165d99f041\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Rob\",\"props\":",[1107,19682,19683],{},"{\"p\":\"payload.name\",\"v\":\"Rob\",\"vt\":\"str\"},{\"p\":\"payload.age\",\"v\":\"46\",\"vt\":\"str\"}",",\"repeat\":\"2\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":570,\"y\":840,\"wires\":[[\"1e0575bd662f9277\"]]},{\"id\":\"e9c0baba4f50b0b2\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"John\",\"props\":",[1107,19686,19687],{},"{\"p\":\"payload.name\",\"v\":\"John\",\"vt\":\"str\"},{\"p\":\"payload.age\",\"v\":\"29\",\"vt\":\"str\"}",",\"repeat\":\"2\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":570,\"y\":880,\"wires\":[[\"1e0575bd662f9277\"]]},{\"id\":\"d5f688682206d316\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":",[1107,19690,19691],{},"{\"t\":\"set\",\"p\":\"age\",\"pt\":\"flow\",\"to\":\"payload.age\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":850,\"y\":860,\"wires\":[[\"d56225e59bde93cf\"]]},{\"id\":\"d56225e59bde93cf\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":",[1107,19694,19695],{},"{\"t\":\"delete\",\"p\":\"payload.age\",\"pt\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1050,\"y\":860,\"wires\":[[\"d5a119b311bf8377\"]]},{\"id\":\"e3ec11b4e038e564\",\"type\":\"debug\",\"z\":\"67746003c844dbc4\",\"name\":\"debug 8\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1080,\"y\":940,\"wires\":",[1107,19698],{},"},{\"id\":\"d5a119b311bf8377\",\"type\":\"delay\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"pauseType\":\"random\",\"timeout\":\"5\",\"timeoutUnits\":\"seconds\",\"rate\":\"1\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"0\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":720,\"y\":940,\"wires\":[[\"b329d7f2685d0ed6\"]]},{\"id\":\"b329d7f2685d0ed6\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":",[1107,19701,19702],{},"{\"t\":\"set\",\"p\":\"payload.age\",\"pt\":\"msg\",\"to\":\"age\",\"tot\":\"flow\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":940,\"wires\":[[\"e3ec11b4e038e564\"]]},{\"id\":\"1e0575bd662f9277\",\"type\":\"delay\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"pauseType\":\"random\",\"timeout\":\"5\",\"timeoutUnits\":\"seconds\",\"rate\":\"1\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"0\",\"randomLast\":\"1\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":700,\"y\":860,\"wires\":[[\"d5f688682206d316\"]]}",[1165,19705,1425],{"id":1424},[15,19707,19708],{},"Storing transactional data in msg rather than context in Node-RED offers advantages in modularity, scalability, simplicity, and also helps prevent race conditions. By using msg, data transfer between nodes becomes more seamless, allowing for independent node reuse across flows without additional configuration. This approach ensures scalability by avoiding the imposition of large datasets on the global context. Moreover, storing data outside of msg.payload within the msg object enhances flexibility. It separates metadata and other relevant information from the main payload, promoting a cleaner and more organized structure. This practice not only aligns with Node-RED's visual programming paradigm but also improves code readability. Steering clear of context for transactional data, while also organizing it within the msg object, provides a comprehensive and reliable solution within the Node-RED framework.",[15,19710,19711,19712,19717],{},"Thanks to ",[63,19713,19716],{"href":19714,"rel":19715},"https:\u002F\u002Fsunrisesunset.io\u002F",[67],"SunriseSunset"," for creating the useful API we've used in this article.",{"title":103,"searchDepth":104,"depth":104,"links":19719},[19720,19721,19722,19723,19724],{"id":19505,"depth":104,"text":19506},{"id":19537,"depth":104,"text":19538},{"id":19582,"depth":104,"text":19583},{"id":19651,"depth":104,"text":19652},{"id":1424,"depth":104,"text":1425},"2024-02-19","In this article we are going to explain how you can store data outside of msg.payload for later use","\u002Fblog\u002F2024\u002F02\u002Fimages\u002Fstoring-data-getting-started-with-node-red.png",{"excerpt":19729},{"type":12,"value":19730},[19731],[15,19732,19499],{},"\u002Fblog\u002F2024\u002F02\u002Ftaking-it-further-with-node-red",{"title":19492,"description":19726},{"loc":19733},"blog\u002F2024\u002F02\u002Ftaking-it-further-with-node-red","Node-RED is one of the easiest ways to program ever created but everyone needs a little help",[8650,138,7457],"Lf90bJc8iDnUNrQLo3dAEUxwJlkA3J8tm2Jy0mgB1MU",{"id":19741,"title":19742,"authors":19743,"body":19744,"cta":3,"date":21288,"description":21289,"extension":116,"image":21267,"lastUpdated":3,"meta":21290,"navigation":129,"path":21297,"seo":21298,"sitemap":21299,"stem":21300,"subtitle":21301,"tags":21302,"tldr":3,"video":3,"__hash__":21304},"blog\u002Fblog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red.md","Speech-Driven Chatbot System with Node-RED",[144],{"type":12,"value":19745,"toc":21277},[19746,19749,19752,19756,19759,19779,19781,19788,19792,19801,19809,19812,19815,19818,19991,19994,19997,20685,20689,20694,20703,20710,20714,20717,20728,20735,20739,20742,20756,20760,20763,20774,20777,20779,20836,20838,20841,21238,21241,21248,21250,21257,21262,21269,21271,21274],[15,19747,19748],{},"Have you ever wanted to integrate speech recognition and synthesis into your Node-RED project and thought it was too complex? Often it has required external services or APIs. However, in this guide, we show you how you can use speech recognition and synthesis in your Node-RED projects without needing an external service or API.",[15,19750,19751],{},"In addition, we make things more interesting by building a system that can listen to us and respond like humans using the Chat-GPT API.\nLet's get started!",[31,19753,19755],{"id":19754},"what-exactly-is-speech-recognition-and-synthesis","What exactly is speech recognition and synthesis?",[15,19757,19758],{},"Speech recognition is a technology where a device captures spoken words through a microphone, checks against grammar rules and vocabulary, and returns recognized words as text. On the other hand, speech synthesis converts app text into speech and plays it through a device's speaker or audio output. There are many benefits and real-world applications of this technology.",[39,19760,19761,19767,19773],{},[42,19762,19763,19766],{},[19,19764,19765],{},"Hands-Free Operation:"," Using speech recognition technology is often used today to perform tasks such as making calls, sending messages, or controlling smart home devices without the need for physical interaction.",[42,19768,19769,19772],{},[19,19770,19771],{},"Accessibility:"," It allows individuals with visual impairments to access digital content through spoken words and as discussed above, to control devices without physical interaction.",[42,19774,19775,19778],{},[19,19776,19777],{},"Efficient Content Consumption:"," It allows us to listen to information instead of reading. For example, in the audiobook industry by using speech synthesis technology they create audio versions of books which helps users to be more productive.",[31,19780,18600],{"id":18599},[15,19782,19783,19784,19787],{},"Install Dashboard 2.0. Follow these ",[63,19785,19786],{"href":3671},"instructions"," to get up and running.",[31,19789,19791],{"id":19790},"building-speech-to-text-vue-component","Building Speech-to-Text Vue component",[15,19793,19794,19795,19800],{},"In this section, we will build a Vue component that will perform a speech-to-text conversion operation using Web speech API, and display results on the dashboard.  While we did say previously that we won't need any external API for speech recognition, this ",[63,19796,19799],{"href":19797,"rel":19798},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWeb_Speech_API",[67],"Web Speech API"," is not an external API.\nThis will process your speech locally as it is a JavaScript API that allows us to use speech-related functionalities, such as speech recognition and synthesis, in a web browser directly. It is widely present in modern browsers (except Firefox) which eliminates the need for external APIs to implement these features. Let's now start to build that component.",[271,19802,19803,19806],{},[42,19804,19805],{},"Drag a ui template widget to canvas and select the created group.",[42,19807,19808],{},"Paste the below Vue snippets into the template widget step by step.",[15,19810,19811],{},"If you are unfamiliar with Vue, we have added comments that will help you to understand the code better.",[15,19813,19814],{},"We are going to start by pasting a user interface’s snippet which will allow us to interact with our system. This snippet adds a button that triggers our system to listen, an Icon, and a paragraph to display speech recognition results on the dashboard.",[15,19816,19817],{},"{% raw %}",[1756,19819,19821],{"className":17125,"code":19820,"language":8779,"meta":103,"style":103},"\u003Ctemplate>\n  \u003Cdiv style=\"display: flex; flex-direction: column; justify-content: center; align-items: center;\">\n    \u003C!-- Button triggers recording when clicked -->\n    \u003Cbutton @click=\"startRecording\">\n      \u003C!-- Microphone icon inside the button -->\n      \u003Cimg alt=\"Microphone\" style=\"height: 62px; width: 62px\" :src=\"microphoneIcon\">\n    \u003C\u002Fbutton>\n    \u003C!-- Displaying speech recognition results -->\n    \u003Cp> \u003Cstrong>You:\u003C\u002Fstrong> {{ results }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[924,19822,19823,19831,19852,19857,19879,19884,19927,19936,19941,19974,19983],{"__ignoreMap":103},[1107,19824,19825,19827,19829],{"class":1765,"line":1766},[1107,19826,17136],{"class":1783},[1107,19828,17139],{"class":1946},[1107,19830,17142],{"class":1783},[1107,19832,19833,19836,19838,19841,19843,19845,19848,19850],{"class":1765,"line":107},[1107,19834,19835],{"class":1783},"  \u003C",[1107,19837,17286],{"class":1946},[1107,19839,19840],{"class":1775}," style",[1107,19842,1784],{"class":1783},[1107,19844,1970],{"class":1783},[1107,19846,19847],{"class":1966},"display: flex; flex-direction: column; justify-content: center; align-items: center;",[1107,19849,1970],{"class":1783},[1107,19851,17142],{"class":1783},[1107,19853,19854],{"class":1765,"line":294},[1107,19855,19856],{"class":1769},"    \u003C!-- Button triggers recording when clicked -->\n",[1107,19858,19859,19862,19865,19868,19870,19872,19875,19877],{"class":1765,"line":104},[1107,19860,19861],{"class":1783},"    \u003C",[1107,19863,19864],{"class":1946},"button",[1107,19866,19867],{"class":1775}," @click",[1107,19869,1784],{"class":1783},[1107,19871,1970],{"class":1783},[1107,19873,19874],{"class":1966},"startRecording",[1107,19876,1970],{"class":1783},[1107,19878,17142],{"class":1783},[1107,19880,19881],{"class":1765,"line":1868},[1107,19882,19883],{"class":1769},"      \u003C!-- Microphone icon inside the button -->\n",[1107,19885,19886,19889,19891,19894,19896,19898,19901,19903,19905,19907,19909,19912,19914,19916,19918,19920,19923,19925],{"class":1765,"line":1896},[1107,19887,19888],{"class":1783},"      \u003C",[1107,19890,165],{"class":1946},[1107,19892,19893],{"class":1775}," alt",[1107,19895,1784],{"class":1783},[1107,19897,1970],{"class":1783},[1107,19899,19900],{"class":1966},"Microphone",[1107,19902,1970],{"class":1783},[1107,19904,19840],{"class":1775},[1107,19906,1784],{"class":1783},[1107,19908,1970],{"class":1783},[1107,19910,19911],{"class":1966},"height: 62px; width: 62px",[1107,19913,1970],{"class":1783},[1107,19915,17477],{"class":1775},[1107,19917,1784],{"class":1783},[1107,19919,1970],{"class":1783},[1107,19921,19922],{"class":1966},"microphoneIcon",[1107,19924,1970],{"class":1783},[1107,19926,17142],{"class":1783},[1107,19928,19929,19932,19934],{"class":1765,"line":1623},[1107,19930,19931],{"class":1783},"    \u003C\u002F",[1107,19933,19864],{"class":1946},[1107,19935,17142],{"class":1783},[1107,19937,19938],{"class":1765,"line":1924},[1107,19939,19940],{"class":1769},"    \u003C!-- Displaying speech recognition results -->\n",[1107,19942,19943,19945,19947,19949,19952,19954,19956,19959,19961,19963,19965,19968,19970,19972],{"class":1765,"line":1930},[1107,19944,19861],{"class":1783},[1107,19946,15],{"class":1946},[1107,19948,17301],{"class":1783},[1107,19950,19951],{"class":1783}," \u003C",[1107,19953,19],{"class":1946},[1107,19955,17301],{"class":1783},[1107,19957,19958],{"class":1779},"You:",[1107,19960,17307],{"class":1783},[1107,19962,19],{"class":1946},[1107,19964,17301],{"class":1783},[1107,19966,19967],{"class":1779}," {{ results }}",[1107,19969,17307],{"class":1783},[1107,19971,15],{"class":1946},[1107,19973,17142],{"class":1783},[1107,19975,19976,19979,19981],{"class":1765,"line":1940},[1107,19977,19978],{"class":1783},"  \u003C\u002F",[1107,19980,17286],{"class":1946},[1107,19982,17142],{"class":1783},[1107,19984,19985,19987,19989],{"class":1765,"line":1976},[1107,19986,17307],{"class":1783},[1107,19988,17139],{"class":1946},[1107,19990,17142],{"class":1783},[15,19992,19993],{},"{% endraw %}",[15,19995,19996],{},"Now paste the below script right after the HTML in the template widget, This script adds functionality of speech recognition in our system.",[1756,19998,20000],{"className":17125,"code":19999,"language":8779,"meta":103,"style":103},"\u003Cscript>\nexport default {\n data() {\n   return {\n     \u002F\u002F Initial data properties\n     buttonText: 'Speak',\n     microphoneIcon: 'http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png',\n     recognition: null,\n     results: '',\n   };\n },\n methods: {\n   \u002F\u002F Method to start recording\n   startRecording() {\n     this.buttonText = 'Recording';\n     this.recognition.stop();\n     this.recognition.start();\n   },\n   \u002F\u002F Method to process the speech recognition results\n   processSpeech(event) {\n     let results = Array.from(event.results).map(result =>   result[0].transcript).join('');\n     this.results = results;\n     this.$emit('speak', results);\n     \u002F\u002FSending result to next node as payload\n     this.send(results);\n   },\n   \u002F\u002F Method to handle the start of recognition\n   handleRecognitionStart() {\n     this.microphoneIcon = 'https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002F0\u002F06\u002FMic-Animation.gif';\n   },\n   \u002F\u002F Method to handle recognition errors\n   handleRecognitionError(event) {\n     this.microphoneIcon = event.error === 'no-speech' || event.error === 'audio-capture'\n       ? 'https:\u002F\u002Fi.ytimg.com\u002Fvi\u002F9YQU797Oy0Y\u002Fhqdefault.jpg'\n       : this.microphoneIcon;\n   },\n   \u002F\u002F Method to handle the end of recognition\n   handleRecognitionEnd() {\n     this.microphoneIcon = 'http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png';\n   },\n   \u002F\u002F Method to set up the SpeechRecognition object\n   setupRecognition() {\n     this.recognition = new webkitSpeechRecognition();\n     this.recognition.continuous = false;\n     this.recognition.interimResults = false;\n     this.recognition.onresult = this.processSpeech;\n     this.recognition.onstart = this.handleRecognitionStart;\n     this.recognition.onerror = this.handleRecognitionError;\n     this.recognition.onend = this.handleRecognitionEnd;\n   },\n },\n mounted() {\n   \u002F\u002F Initialize SpeechRecognition when the component is mounted\n   this.setupRecognition();\n },\n};\n\u003C\u002Fscript>\n",[924,20001,20002,20010,20019,20028,20035,20040,20056,20072,20082,20093,20098,20102,20111,20116,20125,20144,20159,20173,20178,20183,20197,20267,20279,20303,20308,20323,20327,20332,20341,20358,20362,20367,20380,20425,20437,20449,20453,20458,20467,20483,20487,20492,20501,20518,20537,20554,20574,20594,20614,20634,20638,20642,20651,20656,20668,20672,20677],{"__ignoreMap":103},[1107,20003,20004,20006,20008],{"class":1765,"line":1766},[1107,20005,17136],{"class":1783},[1107,20007,94],{"class":1946},[1107,20009,17142],{"class":1783},[1107,20011,20012,20015,20017],{"class":1765,"line":107},[1107,20013,20014],{"class":1933},"export",[1107,20016,17629],{"class":1933},[1107,20018,10797],{"class":1783},[1107,20020,20021,20024,20026],{"class":1765,"line":294},[1107,20022,20023],{"class":1946}," data",[1107,20025,17639],{"class":1783},[1107,20027,10797],{"class":1783},[1107,20029,20030,20033],{"class":1765,"line":104},[1107,20031,20032],{"class":1933},"   return",[1107,20034,10797],{"class":1783},[1107,20036,20037],{"class":1765,"line":1868},[1107,20038,20039],{"class":1769},"     \u002F\u002F Initial data properties\n",[1107,20041,20042,20045,20047,20049,20052,20054],{"class":1765,"line":1896},[1107,20043,20044],{"class":1946},"     buttonText",[1107,20046,992],{"class":1783},[1107,20048,15373],{"class":1783},[1107,20050,20051],{"class":1966},"Speak",[1107,20053,15363],{"class":1783},[1107,20055,9079],{"class":1783},[1107,20057,20058,20061,20063,20065,20068,20070],{"class":1765,"line":1623},[1107,20059,20060],{"class":1946},"     microphoneIcon",[1107,20062,992],{"class":1783},[1107,20064,15373],{"class":1783},[1107,20066,20067],{"class":1966},"http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png",[1107,20069,15363],{"class":1783},[1107,20071,9079],{"class":1783},[1107,20073,20074,20077,20079],{"class":1765,"line":1924},[1107,20075,20076],{"class":1946},"     recognition",[1107,20078,992],{"class":1783},[1107,20080,20081],{"class":1783}," null,\n",[1107,20083,20084,20087,20089,20091],{"class":1765,"line":1930},[1107,20085,20086],{"class":1946},"     results",[1107,20088,992],{"class":1783},[1107,20090,17663],{"class":1783},[1107,20092,9079],{"class":1783},[1107,20094,20095],{"class":1765,"line":1940},[1107,20096,20097],{"class":1783},"   };\n",[1107,20099,20100],{"class":1765,"line":1976},[1107,20101,1973],{"class":1783},[1107,20103,20104,20107,20109],{"class":1765,"line":2002},[1107,20105,20106],{"class":1946}," methods",[1107,20108,992],{"class":1783},[1107,20110,10797],{"class":1783},[1107,20112,20113],{"class":1765,"line":2029},[1107,20114,20115],{"class":1769},"   \u002F\u002F Method to start recording\n",[1107,20117,20118,20121,20123],{"class":1765,"line":1691},[1107,20119,20120],{"class":1946},"   startRecording",[1107,20122,17639],{"class":1783},[1107,20124,10797],{"class":1783},[1107,20126,20127,20130,20133,20135,20137,20140,20142],{"class":1765,"line":2080},[1107,20128,20129],{"class":1783},"     this.",[1107,20131,20132],{"class":1779},"buttonText",[1107,20134,17724],{"class":1783},[1107,20136,15373],{"class":1783},[1107,20138,20139],{"class":1966},"Recording",[1107,20141,15363],{"class":1783},[1107,20143,1805],{"class":1783},[1107,20145,20146,20148,20151,20153,20155,20157],{"class":1765,"line":7873},[1107,20147,20129],{"class":1783},[1107,20149,20150],{"class":1779},"recognition",[1107,20152,29],{"class":1783},[1107,20154,959],{"class":15356},[1107,20156,17639],{"class":1946},[1107,20158,1805],{"class":1783},[1107,20160,20161,20163,20165,20167,20169,20171],{"class":1765,"line":7879},[1107,20162,20129],{"class":1783},[1107,20164,20150],{"class":1779},[1107,20166,29],{"class":1783},[1107,20168,953],{"class":15356},[1107,20170,17639],{"class":1946},[1107,20172,1805],{"class":1783},[1107,20174,20175],{"class":1765,"line":7885},[1107,20176,20177],{"class":1783},"   },\n",[1107,20179,20180],{"class":1765,"line":7891},[1107,20181,20182],{"class":1769},"   \u002F\u002F Method to process the speech recognition results\n",[1107,20184,20185,20188,20190,20193,20195],{"class":1765,"line":7897},[1107,20186,20187],{"class":1946},"   processSpeech",[1107,20189,15360],{"class":1783},[1107,20191,20192],{"class":16960},"event",[1107,20194,8879],{"class":1783},[1107,20196,10797],{"class":1783},[1107,20198,20199,20202,20205,20207,20210,20212,20215,20217,20219,20221,20224,20226,20228,20231,20233,20236,20238,20241,20243,20245,20247,20249,20252,20254,20256,20258,20260,20263,20265],{"class":1765,"line":7902},[1107,20200,20201],{"class":1775},"     let",[1107,20203,20204],{"class":1779}," results",[1107,20206,17724],{"class":1783},[1107,20208,20209],{"class":1779}," Array",[1107,20211,29],{"class":1783},[1107,20213,20214],{"class":15356},"from",[1107,20216,15360],{"class":1946},[1107,20218,20192],{"class":1779},[1107,20220,29],{"class":1783},[1107,20222,20223],{"class":1779},"results",[1107,20225,8879],{"class":1946},[1107,20227,29],{"class":1783},[1107,20229,20230],{"class":15356},"map",[1107,20232,15360],{"class":1946},[1107,20234,20235],{"class":16960},"result",[1107,20237,16966],{"class":1775},[1107,20239,20240],{"class":1779},"   result",[1107,20242,8882],{"class":1946},[1107,20244,1855],{"class":1854},[1107,20246,1858],{"class":1946},[1107,20248,29],{"class":1783},[1107,20250,20251],{"class":1779},"transcript",[1107,20253,8879],{"class":1946},[1107,20255,29],{"class":1783},[1107,20257,11515],{"class":15356},[1107,20259,15360],{"class":1946},[1107,20261,20262],{"class":1783},"''",[1107,20264,8879],{"class":1946},[1107,20266,1805],{"class":1783},[1107,20268,20269,20271,20273,20275,20277],{"class":1765,"line":7908},[1107,20270,20129],{"class":1783},[1107,20272,20223],{"class":1779},[1107,20274,17724],{"class":1783},[1107,20276,20204],{"class":1779},[1107,20278,1805],{"class":1783},[1107,20280,20281,20283,20286,20288,20290,20293,20295,20297,20299,20301],{"class":1765,"line":7914},[1107,20282,20129],{"class":1783},[1107,20284,20285],{"class":15356},"$emit",[1107,20287,15360],{"class":1946},[1107,20289,15363],{"class":1783},[1107,20291,20292],{"class":1966},"speak",[1107,20294,15363],{"class":1783},[1107,20296,1955],{"class":1783},[1107,20298,20204],{"class":1779},[1107,20300,8879],{"class":1946},[1107,20302,1805],{"class":1783},[1107,20304,20305],{"class":1765,"line":7919},[1107,20306,20307],{"class":1769},"     \u002F\u002FSending result to next node as payload\n",[1107,20309,20310,20312,20315,20317,20319,20321],{"class":1765,"line":7925},[1107,20311,20129],{"class":1783},[1107,20313,20314],{"class":15356},"send",[1107,20316,15360],{"class":1946},[1107,20318,20223],{"class":1779},[1107,20320,8879],{"class":1946},[1107,20322,1805],{"class":1783},[1107,20324,20325],{"class":1765,"line":7931},[1107,20326,20177],{"class":1783},[1107,20328,20329],{"class":1765,"line":7937},[1107,20330,20331],{"class":1769},"   \u002F\u002F Method to handle the start of recognition\n",[1107,20333,20334,20337,20339],{"class":1765,"line":7943},[1107,20335,20336],{"class":1946},"   handleRecognitionStart",[1107,20338,17639],{"class":1783},[1107,20340,10797],{"class":1783},[1107,20342,20343,20345,20347,20349,20351,20354,20356],{"class":1765,"line":7949},[1107,20344,20129],{"class":1783},[1107,20346,19922],{"class":1779},[1107,20348,17724],{"class":1783},[1107,20350,15373],{"class":1783},[1107,20352,20353],{"class":1966},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002F0\u002F06\u002FMic-Animation.gif",[1107,20355,15363],{"class":1783},[1107,20357,1805],{"class":1783},[1107,20359,20360],{"class":1765,"line":7955},[1107,20361,20177],{"class":1783},[1107,20363,20364],{"class":1765,"line":7961},[1107,20365,20366],{"class":1769},"   \u002F\u002F Method to handle recognition errors\n",[1107,20368,20369,20372,20374,20376,20378],{"class":1765,"line":7966},[1107,20370,20371],{"class":1946},"   handleRecognitionError",[1107,20373,15360],{"class":1783},[1107,20375,20192],{"class":16960},[1107,20377,8879],{"class":1783},[1107,20379,10797],{"class":1783},[1107,20381,20382,20384,20386,20388,20391,20393,20396,20399,20401,20404,20406,20409,20411,20413,20415,20417,20419,20422],{"class":1765,"line":7972},[1107,20383,20129],{"class":1783},[1107,20385,19922],{"class":1779},[1107,20387,17724],{"class":1783},[1107,20389,20390],{"class":1779}," event",[1107,20392,29],{"class":1783},[1107,20394,20395],{"class":1779},"error",[1107,20397,20398],{"class":1783}," ===",[1107,20400,15373],{"class":1783},[1107,20402,20403],{"class":1966},"no-speech",[1107,20405,15363],{"class":1783},[1107,20407,20408],{"class":1783}," ||",[1107,20410,20390],{"class":1779},[1107,20412,29],{"class":1783},[1107,20414,20395],{"class":1779},[1107,20416,20398],{"class":1783},[1107,20418,15373],{"class":1783},[1107,20420,20421],{"class":1966},"audio-capture",[1107,20423,20424],{"class":1783},"'\n",[1107,20426,20427,20430,20432,20435],{"class":1765,"line":7978},[1107,20428,20429],{"class":1783},"       ?",[1107,20431,15373],{"class":1783},[1107,20433,20434],{"class":1966},"https:\u002F\u002Fi.ytimg.com\u002Fvi\u002F9YQU797Oy0Y\u002Fhqdefault.jpg",[1107,20436,20424],{"class":1783},[1107,20438,20439,20442,20445,20447],{"class":1765,"line":7984},[1107,20440,20441],{"class":1783},"       :",[1107,20443,20444],{"class":1783}," this.",[1107,20446,19922],{"class":1779},[1107,20448,1805],{"class":1783},[1107,20450,20451],{"class":1765,"line":7990},[1107,20452,20177],{"class":1783},[1107,20454,20455],{"class":1765,"line":7995},[1107,20456,20457],{"class":1769},"   \u002F\u002F Method to handle the end of recognition\n",[1107,20459,20460,20463,20465],{"class":1765,"line":8001},[1107,20461,20462],{"class":1946},"   handleRecognitionEnd",[1107,20464,17639],{"class":1783},[1107,20466,10797],{"class":1783},[1107,20468,20469,20471,20473,20475,20477,20479,20481],{"class":1765,"line":8007},[1107,20470,20129],{"class":1783},[1107,20472,19922],{"class":1779},[1107,20474,17724],{"class":1783},[1107,20476,15373],{"class":1783},[1107,20478,20067],{"class":1966},[1107,20480,15363],{"class":1783},[1107,20482,1805],{"class":1783},[1107,20484,20485],{"class":1765,"line":8013},[1107,20486,20177],{"class":1783},[1107,20488,20489],{"class":1765,"line":8019},[1107,20490,20491],{"class":1769},"   \u002F\u002F Method to set up the SpeechRecognition object\n",[1107,20493,20494,20497,20499],{"class":1765,"line":8024},[1107,20495,20496],{"class":1946},"   setupRecognition",[1107,20498,17639],{"class":1783},[1107,20500,10797],{"class":1783},[1107,20502,20503,20505,20507,20509,20511,20514,20516],{"class":1765,"line":8030},[1107,20504,20129],{"class":1783},[1107,20506,20150],{"class":1779},[1107,20508,17724],{"class":1783},[1107,20510,17760],{"class":1783},[1107,20512,20513],{"class":15356}," webkitSpeechRecognition",[1107,20515,17639],{"class":1946},[1107,20517,1805],{"class":1783},[1107,20519,20520,20522,20524,20526,20529,20531,20535],{"class":1765,"line":8036},[1107,20521,20129],{"class":1783},[1107,20523,20150],{"class":1779},[1107,20525,29],{"class":1783},[1107,20527,20528],{"class":1779},"continuous",[1107,20530,17724],{"class":1783},[1107,20532,20534],{"class":20533},"sfNiH"," false",[1107,20536,1805],{"class":1783},[1107,20538,20539,20541,20543,20545,20548,20550,20552],{"class":1765,"line":8041},[1107,20540,20129],{"class":1783},[1107,20542,20150],{"class":1779},[1107,20544,29],{"class":1783},[1107,20546,20547],{"class":1779},"interimResults",[1107,20549,17724],{"class":1783},[1107,20551,20534],{"class":20533},[1107,20553,1805],{"class":1783},[1107,20555,20556,20558,20560,20562,20565,20567,20569,20572],{"class":1765,"line":8047},[1107,20557,20129],{"class":1783},[1107,20559,20150],{"class":1779},[1107,20561,29],{"class":1783},[1107,20563,20564],{"class":1779},"onresult",[1107,20566,17724],{"class":1783},[1107,20568,20444],{"class":1783},[1107,20570,20571],{"class":1779},"processSpeech",[1107,20573,1805],{"class":1783},[1107,20575,20576,20578,20580,20582,20585,20587,20589,20592],{"class":1765,"line":8053},[1107,20577,20129],{"class":1783},[1107,20579,20150],{"class":1779},[1107,20581,29],{"class":1783},[1107,20583,20584],{"class":1779},"onstart",[1107,20586,17724],{"class":1783},[1107,20588,20444],{"class":1783},[1107,20590,20591],{"class":1779},"handleRecognitionStart",[1107,20593,1805],{"class":1783},[1107,20595,20596,20598,20600,20602,20605,20607,20609,20612],{"class":1765,"line":8059},[1107,20597,20129],{"class":1783},[1107,20599,20150],{"class":1779},[1107,20601,29],{"class":1783},[1107,20603,20604],{"class":1779},"onerror",[1107,20606,17724],{"class":1783},[1107,20608,20444],{"class":1783},[1107,20610,20611],{"class":1779},"handleRecognitionError",[1107,20613,1805],{"class":1783},[1107,20615,20616,20618,20620,20622,20625,20627,20629,20632],{"class":1765,"line":8064},[1107,20617,20129],{"class":1783},[1107,20619,20150],{"class":1779},[1107,20621,29],{"class":1783},[1107,20623,20624],{"class":1779},"onend",[1107,20626,17724],{"class":1783},[1107,20628,20444],{"class":1783},[1107,20630,20631],{"class":1779},"handleRecognitionEnd",[1107,20633,1805],{"class":1783},[1107,20635,20636],{"class":1765,"line":8069},[1107,20637,20177],{"class":1783},[1107,20639,20640],{"class":1765,"line":8075},[1107,20641,1973],{"class":1783},[1107,20643,20644,20647,20649],{"class":1765,"line":8081},[1107,20645,20646],{"class":1946}," mounted",[1107,20648,17639],{"class":1783},[1107,20650,10797],{"class":1783},[1107,20652,20653],{"class":1765,"line":8087},[1107,20654,20655],{"class":1769},"   \u002F\u002F Initialize SpeechRecognition when the component is mounted\n",[1107,20657,20658,20661,20664,20666],{"class":1765,"line":8092},[1107,20659,20660],{"class":1783},"   this.",[1107,20662,20663],{"class":15356},"setupRecognition",[1107,20665,17639],{"class":1946},[1107,20667,1805],{"class":1783},[1107,20669,20670],{"class":1765,"line":8098},[1107,20671,1973],{"class":1783},[1107,20673,20674],{"class":1765,"line":8104},[1107,20675,20676],{"class":1783},"};\n",[1107,20678,20679,20681,20683],{"class":1765,"line":8109},[1107,20680,17307],{"class":1783},[1107,20682,94],{"class":1946},[1107,20684,17142],{"class":1783},[31,20686,20688],{"id":20687},"adding-an-environment-variable","Adding an Environment variable",[15,20690,20691,20692],{},"Why do we need to add an environment variable? In this guide, we will build a speech-driven chatbot that involves integrating the Chat-GPT AI model. For this we need openAi’s API key. An API key is a form of private data that needs to be protected from being exposed. That is why we need the environment variables. It provides a secure way to store and access the API key without revealing it directly in the flow.  For more details see ",[63,20693,14725],{"href":14312},[271,20695,20696,20698],{},[42,20697,18696],{},[42,20699,16614,20700,20702],{},[924,20701,18701],{}," button and add a variable for Chat-gpt API.",[15,20704,20705],{},[165,20706],{"alt":20707,"src":20708,"title":20709},"\"Setting environment variable for Chat-gpt token\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-environment-section.png","Setting environment variable for chat-gpt token",[31,20711,20713],{"id":20712},"setting-msg-property","Setting msg property",[15,20715,20716],{},"Now let’s set that added environment variables as msg's property.",[271,20718,20719,20722,20725],{},[42,20720,20721],{},"Add a change node to canvas.",[42,20723,20724],{},"Set environment variable to ms.token property.",[42,20726,20727],{},"Connect the change node’s input to the template widget’s output.",[15,20729,20730],{},[165,20731],{"alt":20732,"src":20733,"title":20734},"\"Setting msg's property for Chat-gpt token\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-change-node.png","Setting msg's property for Chat-gpt token",[31,20736,20738],{"id":20737},"installing-and-configuring-custom-node","Installing and configuring custom node",[15,20740,20741],{},"In this section, we will install a custom node that will allow us to interact with the Chat-gpt AI model.",[271,20743,20744,20750,20753],{},[42,20745,18616,20746,20749],{},[924,20747,20748],{},"@sumit_shinde_84\u002Fnode-red-contrib-node-gpt"," by pallet manager, you can use other nodes according to your preference.",[42,20751,20752],{},"Drag a ChatGPT node to canvas.",[42,20754,20755],{},"Connect the ChatGPT node’s input to the change node’s output.",[31,20757,20759],{"id":20758},"building-text-to-speech-vue-component","Building Text-to-Speech Vue component",[15,20761,20762],{},"We will build a Vue component that converts text received from ChatGPT into speech.",[271,20764,20765,20768,20771],{},[42,20766,20767],{},"Drag another template widget to canvas and select the added group, alternatively, you can create a separate group for this component according to your preference.",[42,20769,20770],{},"Paste the below Vue snippets into the template widget.",[42,20772,20773],{},"Connect the template widget’s input to the ChatGPT node’s output.",[15,20775,20776],{},"Paste the below snippet in the template widget which displays chat-gpt response on the dashboard",[15,20778,19817],{},[1756,20780,20782],{"className":17125,"code":20781,"language":8779,"meta":103,"style":103},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cstrong> Chat-gpt: \u003C\u002Fstrong> {{textToSpeech}}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[924,20783,20784,20792,20800,20820,20828],{"__ignoreMap":103},[1107,20785,20786,20788,20790],{"class":1765,"line":1766},[1107,20787,17136],{"class":1783},[1107,20789,17139],{"class":1946},[1107,20791,17142],{"class":1783},[1107,20793,20794,20796,20798],{"class":1765,"line":107},[1107,20795,19835],{"class":1783},[1107,20797,17286],{"class":1946},[1107,20799,17142],{"class":1783},[1107,20801,20802,20804,20806,20808,20811,20813,20815,20817],{"class":1765,"line":294},[1107,20803,19861],{"class":1783},[1107,20805,19],{"class":1946},[1107,20807,17301],{"class":1783},[1107,20809,20810],{"class":1779}," Chat-gpt: ",[1107,20812,17307],{"class":1783},[1107,20814,19],{"class":1946},[1107,20816,17301],{"class":1783},[1107,20818,20819],{"class":1779}," {{textToSpeech}}\n",[1107,20821,20822,20824,20826],{"class":1765,"line":104},[1107,20823,19978],{"class":1783},[1107,20825,17286],{"class":1946},[1107,20827,17142],{"class":1783},[1107,20829,20830,20832,20834],{"class":1765,"line":1868},[1107,20831,17307],{"class":1783},[1107,20833,17139],{"class":1946},[1107,20835,17142],{"class":1783},[15,20837,19993],{},[15,20839,20840],{},"Paste the below snippet right after the HTML, This snippet adds the functionality of text-to-speech into our system, which triggers when msg received by the previous node.",[1756,20842,20844],{"className":17125,"code":20843,"language":8779,"meta":103,"style":103},"\u003Cscript>\n  export default {\n  data() {\n    return {\n      \u002F\u002F Data property to store the text to be spoken\n      textToSpeech: '',\n    };\n  },\n  methods: {\n    \u002F\u002F Method to trigger text-to-speech\n    speakText() {\n      \u002F\u002F Check if there is non-empty text to speech\n      if (this.textToSpeech.trim() !== '') {\n        \u002F\u002F Create a SpeechSynthesisUtterance with the text to be spoken\n        const utterance = new  SpeechSynthesisUtterance(this.textToSpeech);\n        \u002F\u002F Use the SpeechSynthesis API to speak the provided text\n        window.speechSynthesis.speak(utterance);\n      }\n    },\n  },\n  mounted() {\n    \u002F\u002F Event listener for receiving messages    \n  \n     this.$socket.on('msg-input:' + this.id, (msg) => {\n\n      \u002F\u002F Update the textToSpeech property with the received message payload\n      this.textToSpeech = msg.payload;\n     \n      \u002F\u002F Trigger text-to-speech with the received message\n      this.speakText();\n    });\n  },\n};\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n  textarea {\n    width: 100%;\n    height: 100px;\n    margin-bottom: 10px;\n  }\n\u003C\u002Fstyle>\n",[924,20845,20846,20854,20863,20872,20879,20884,20895,20900,20905,20914,20919,20928,20933,20964,20969,20994,20999,21022,21027,21032,21036,21045,21050,21055,21096,21100,21105,21122,21127,21132,21143,21152,21156,21160,21168,21172,21182,21189,21201,21213,21225,21230],{"__ignoreMap":103},[1107,20847,20848,20850,20852],{"class":1765,"line":1766},[1107,20849,17136],{"class":1783},[1107,20851,94],{"class":1946},[1107,20853,17142],{"class":1783},[1107,20855,20856,20859,20861],{"class":1765,"line":107},[1107,20857,20858],{"class":1933},"  export",[1107,20860,17629],{"class":1933},[1107,20862,10797],{"class":1783},[1107,20864,20865,20868,20870],{"class":1765,"line":294},[1107,20866,20867],{"class":1946},"  data",[1107,20869,17639],{"class":1783},[1107,20871,10797],{"class":1783},[1107,20873,20874,20877],{"class":1765,"line":104},[1107,20875,20876],{"class":1933},"    return",[1107,20878,10797],{"class":1783},[1107,20880,20881],{"class":1765,"line":1868},[1107,20882,20883],{"class":1769},"      \u002F\u002F Data property to store the text to be spoken\n",[1107,20885,20886,20889,20891,20893],{"class":1765,"line":1896},[1107,20887,20888],{"class":1946},"      textToSpeech",[1107,20890,992],{"class":1783},[1107,20892,17663],{"class":1783},[1107,20894,9079],{"class":1783},[1107,20896,20897],{"class":1765,"line":1623},[1107,20898,20899],{"class":1783},"    };\n",[1107,20901,20902],{"class":1765,"line":1924},[1107,20903,20904],{"class":1783},"  },\n",[1107,20906,20907,20910,20912],{"class":1765,"line":1930},[1107,20908,20909],{"class":1946},"  methods",[1107,20911,992],{"class":1783},[1107,20913,10797],{"class":1783},[1107,20915,20916],{"class":1765,"line":1940},[1107,20917,20918],{"class":1769},"    \u002F\u002F Method to trigger text-to-speech\n",[1107,20920,20921,20924,20926],{"class":1765,"line":1976},[1107,20922,20923],{"class":1946},"    speakText",[1107,20925,17639],{"class":1783},[1107,20927,10797],{"class":1783},[1107,20929,20930],{"class":1765,"line":2002},[1107,20931,20932],{"class":1769},"      \u002F\u002F Check if there is non-empty text to speech\n",[1107,20934,20935,20938,20941,20944,20947,20949,20952,20955,20958,20960,20962],{"class":1765,"line":2029},[1107,20936,20937],{"class":1933},"      if",[1107,20939,20940],{"class":1946}," (",[1107,20942,20943],{"class":1783},"this.",[1107,20945,20946],{"class":1779},"textToSpeech",[1107,20948,29],{"class":1783},[1107,20950,20951],{"class":15356},"trim",[1107,20953,20954],{"class":1946},"() ",[1107,20956,20957],{"class":1783},"!==",[1107,20959,17663],{"class":1783},[1107,20961,16683],{"class":1946},[1107,20963,9062],{"class":1783},[1107,20965,20966],{"class":1765,"line":1691},[1107,20967,20968],{"class":1769},"        \u002F\u002F Create a SpeechSynthesisUtterance with the text to be spoken\n",[1107,20970,20971,20974,20977,20979,20981,20984,20986,20988,20990,20992],{"class":1765,"line":2080},[1107,20972,20973],{"class":1775},"        const",[1107,20975,20976],{"class":1779}," utterance",[1107,20978,17724],{"class":1783},[1107,20980,17760],{"class":1783},[1107,20982,20983],{"class":15356},"  SpeechSynthesisUtterance",[1107,20985,15360],{"class":1946},[1107,20987,20943],{"class":1783},[1107,20989,20946],{"class":1779},[1107,20991,8879],{"class":1946},[1107,20993,1805],{"class":1783},[1107,20995,20996],{"class":1765,"line":7873},[1107,20997,20998],{"class":1769},"        \u002F\u002F Use the SpeechSynthesis API to speak the provided text\n",[1107,21000,21001,21004,21006,21009,21011,21013,21015,21018,21020],{"class":1765,"line":7879},[1107,21002,21003],{"class":1779},"        window",[1107,21005,29],{"class":1783},[1107,21007,21008],{"class":1779},"speechSynthesis",[1107,21010,29],{"class":1783},[1107,21012,20292],{"class":15356},[1107,21014,15360],{"class":1946},[1107,21016,21017],{"class":1779},"utterance",[1107,21019,8879],{"class":1946},[1107,21021,1805],{"class":1783},[1107,21023,21024],{"class":1765,"line":7885},[1107,21025,21026],{"class":1783},"      }\n",[1107,21028,21029],{"class":1765,"line":7891},[1107,21030,21031],{"class":1783},"    },\n",[1107,21033,21034],{"class":1765,"line":7897},[1107,21035,20904],{"class":1783},[1107,21037,21038,21041,21043],{"class":1765,"line":7902},[1107,21039,21040],{"class":1946},"  mounted",[1107,21042,17639],{"class":1783},[1107,21044,10797],{"class":1783},[1107,21046,21047],{"class":1765,"line":7908},[1107,21048,21049],{"class":1769},"    \u002F\u002F Event listener for receiving messages    \n",[1107,21051,21052],{"class":1765,"line":7914},[1107,21053,21054],{"class":1946},"  \n",[1107,21056,21057,21059,21062,21064,21067,21069,21071,21074,21076,21079,21081,21084,21086,21088,21090,21092,21094],{"class":1765,"line":7919},[1107,21058,20129],{"class":1783},[1107,21060,21061],{"class":1779},"$socket",[1107,21063,29],{"class":1783},[1107,21065,21066],{"class":15356},"on",[1107,21068,15360],{"class":1946},[1107,21070,15363],{"class":1783},[1107,21072,21073],{"class":1966},"msg-input:",[1107,21075,15363],{"class":1783},[1107,21077,21078],{"class":1783}," +",[1107,21080,20444],{"class":1783},[1107,21082,21083],{"class":1779},"id",[1107,21085,1955],{"class":1783},[1107,21087,20940],{"class":1783},[1107,21089,14911],{"class":16960},[1107,21091,8879],{"class":1783},[1107,21093,16966],{"class":1775},[1107,21095,10797],{"class":1783},[1107,21097,21098],{"class":1765,"line":7925},[1107,21099,1921],{"emptyLinePlaceholder":129},[1107,21101,21102],{"class":1765,"line":7931},[1107,21103,21104],{"class":1769},"      \u002F\u002F Update the textToSpeech property with the received message payload\n",[1107,21106,21107,21110,21112,21114,21116,21118,21120],{"class":1765,"line":7937},[1107,21108,21109],{"class":1783},"      this.",[1107,21111,20946],{"class":1779},[1107,21113,17724],{"class":1783},[1107,21115,1787],{"class":1779},[1107,21117,29],{"class":1783},[1107,21119,1792],{"class":1779},[1107,21121,1805],{"class":1783},[1107,21123,21124],{"class":1765,"line":7943},[1107,21125,21126],{"class":1946},"     \n",[1107,21128,21129],{"class":1765,"line":7949},[1107,21130,21131],{"class":1769},"      \u002F\u002F Trigger text-to-speech with the received message\n",[1107,21133,21134,21136,21139,21141],{"class":1765,"line":7955},[1107,21135,21109],{"class":1783},[1107,21137,21138],{"class":15356},"speakText",[1107,21140,17639],{"class":1946},[1107,21142,1805],{"class":1783},[1107,21144,21145,21148,21150],{"class":1765,"line":7961},[1107,21146,21147],{"class":1783},"    }",[1107,21149,8879],{"class":1946},[1107,21151,1805],{"class":1783},[1107,21153,21154],{"class":1765,"line":7966},[1107,21155,20904],{"class":1783},[1107,21157,21158],{"class":1765,"line":7972},[1107,21159,20676],{"class":1783},[1107,21161,21162,21164,21166],{"class":1765,"line":7978},[1107,21163,17307],{"class":1783},[1107,21165,94],{"class":1946},[1107,21167,17142],{"class":1783},[1107,21169,21170],{"class":1765,"line":7984},[1107,21171,1921],{"emptyLinePlaceholder":129},[1107,21173,21174,21176,21178,21180],{"class":1765,"line":7990},[1107,21175,17136],{"class":1783},[1107,21177,2582],{"class":1946},[1107,21179,17885],{"class":1775},[1107,21181,17142],{"class":1783},[1107,21183,21184,21187],{"class":1765,"line":7995},[1107,21185,21186],{"class":10543},"  textarea",[1107,21188,10797],{"class":1783},[1107,21190,21191,21194,21196,21199],{"class":1765,"line":8001},[1107,21192,21193],{"class":17906},"    width",[1107,21195,992],{"class":1783},[1107,21197,21198],{"class":1854}," 100%",[1107,21200,1805],{"class":1783},[1107,21202,21203,21206,21208,21211],{"class":1765,"line":8007},[1107,21204,21205],{"class":17906},"    height",[1107,21207,992],{"class":1783},[1107,21209,21210],{"class":1854}," 100px",[1107,21212,1805],{"class":1783},[1107,21214,21215,21218,21220,21223],{"class":1765,"line":8013},[1107,21216,21217],{"class":17906},"    margin-bottom",[1107,21219,992],{"class":1783},[1107,21221,21222],{"class":1854}," 10px",[1107,21224,1805],{"class":1783},[1107,21226,21227],{"class":1765,"line":8019},[1107,21228,21229],{"class":1783},"  }\n",[1107,21231,21232,21234,21236],{"class":1765,"line":8024},[1107,21233,17307],{"class":1783},[1107,21235,2582],{"class":1946},[1107,21237,17142],{"class":1783},[15,21239,21240],{},"Your final flow should look like this:",[15,21242,21243],{},[165,21244],{"alt":21245,"src":21246,"title":21247},"\"Speech Driven Chatbot system flow\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-flow.png","Speech Driven Chatbot system flow",[31,21249,14999],{"id":14998},[15,21251,21252],{},[165,21253],{"alt":21254,"src":21255,"title":21256},"\"Deploying Sentiment analysis Node-RED flow\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-flowfue-editor.png","Deploying Sentiment analysis Node-RED flow",[15,21258,21259,21260],{},"We have successfully built our Speech-Driven Chatbot System. Now it's time to deploy the flow, to do that click on the red deploy button which you can find in the top right corner. After that go to ",[924,21261,18874],{},[15,21263,21264],{},[165,21265],{"alt":21266,"src":21267,"title":21268},"\"Speech Driven Chatbot using Node-RED Dashboard 2.0\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-system.gif","Speech Driven Chatbot using Node-RED Dashboard 2.0",[31,21270,1425],{"id":1424},[15,21272,21273],{},"In this guide, we have built a Speech-Driven Chatbot System which allows us to understand how we can add speech recognition and synthesis features into our project without any external API or custom node. It also provides a brief overview of how we can integrate chat-gpt into our system.",[2582,21275,21276],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}",{"title":103,"searchDepth":104,"depth":104,"links":21278},[21279,21280,21281,21282,21283,21284,21285,21286,21287],{"id":19754,"depth":107,"text":19755},{"id":18599,"depth":107,"text":18600},{"id":19790,"depth":107,"text":19791},{"id":20687,"depth":107,"text":20688},{"id":20712,"depth":107,"text":20713},{"id":20737,"depth":107,"text":20738},{"id":20758,"depth":107,"text":20759},{"id":14998,"depth":107,"text":14999},{"id":1424,"depth":107,"text":1425},"2024-01-29","Learn to build a speech-driven chatbot system with Node-RED and Dashboard 2.0. Integrate speech recognition, synthesis, and Chat-GPT seamlessly.",{"excerpt":21291},{"type":12,"value":21292},[21293,21295],[15,21294,19748],{},[15,21296,19751],{},"\u002Fblog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red",{"title":19742,"description":21289},{"loc":21297},"blog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red","Guide to building speech-driven chatbot using Node-RED, speech recognition, and Dashboard 2.0.",[8650,138,12631,21303,2784],"virtual assistant","3nsKTTaguSWWzjdPFIYFRNL7Q1js-r5OdBfdoMaRdm0",{"id":21306,"title":21307,"authors":21308,"body":21309,"cta":3,"date":21857,"description":21858,"extension":116,"image":21837,"lastUpdated":3,"meta":21859,"navigation":129,"path":21864,"seo":21865,"sitemap":21866,"stem":21867,"subtitle":21868,"tags":21869,"tldr":3,"video":3,"__hash__":21871},"blog\u002Fblog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red.md","Sentiment Analysis with Node-RED",[144],{"type":12,"value":21310,"toc":21847},[21311,21314,21318,21321,21341,21345,21348,21362,21369,21373,21376,21396,21403,21407,21414,21427,21431,21434,21448,21455,21459,21462,21470,21478,21781,21811,21813,21820,21822,21827,21832,21839,21841,21844],[15,21312,21313],{},"Have you ever built a sentiment analysis system to extract insights from text content? If yes then I don’t think you'll need an explanation of how complex it is to build. In this guide, we will build a sentiment analysis system with Node-RED using Dashboard 2.0 in a few easy steps.",[31,21315,21317],{"id":21316},"what-exactly-is-sentiment-analysis","What exactly is sentiment analysis?",[15,21319,21320],{},"Sentiment analysis is a context-mining technique used to understand emotions and opinions expressed in text, often classifying them as positive, neutral, or negative. There are many real-world applications of this technique.",[39,21322,21323,21329,21335],{},[42,21324,21325,21328],{},[19,21326,21327],{},"Analysing Feedback:"," Customers, or other stakeholders like employees, are periodically requested to fill out a feedback form. Analysis of such feedback is the most widespread application of sentiment analysis.",[42,21330,21331,21334],{},[19,21332,21333],{},"Campaign Monitoring:"," Another use case of sentiment analysis is a measure of influence which is crucial in any marketing campaign.",[42,21336,21337,21340],{},[19,21338,21339],{},"Brand Monitoring:"," Brand monitoring is another great use case for sentiment analysis. Companies can use sentiment analysis to check the social media sentiments around their brand from their audience.",[31,21342,21344],{"id":21343},"building-a-form-in-dashboard-20","Building a Form in Dashboard 2.0",[15,21346,21347],{},"In this system, we will analyse the sentiment of text content obtained from the user.  For this we are going to build a user interface using Dashboard 2.0 and Node-RED.",[271,21349,21350,21356,21359],{},[42,21351,21352,21353,21355],{},"Install Node-RED Dashboard 2.0. Follow these ",[63,21354,19786],{"href":3671}," to install.",[42,21357,21358],{},"Drag a ui form widget to the canvas and select the created group.",[42,21360,21361],{},"Add an element in the form widget and give it a name and label, select the type as multiline, and set the number of rows according to your need.",[15,21363,21364],{},[165,21365],{"alt":21366,"src":21367,"title":21368},"\"Taking user input for Sentiment analysis using form\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-form.png","Taking user input for Sentiment analysis using form",[31,21370,21372],{"id":21371},"normalizing-the-data","Normalizing the data",[15,21374,21375],{},"We need to normalize the payload before sending it to the next node because the form widget always returns an object containing the property of values of form elements.",[271,21377,21378,21381,21393],{},[42,21379,21380],{},"Drag a change node to canvas.",[42,21382,1224,21383,1242,21386,21388,21389,21392],{},[924,21384,21385],{},"msg.payload.$FORM_ELEMENT_NAME",[924,21387,1227],{},", replace the ",[924,21390,21391],{},"$FORM_ELEMENT_NAME"," with the name of the form element that you have added to the form to obtain user input.",[42,21394,21395],{},"Connect the UI form nodes output to the change node’s input.",[15,21397,21398],{},[165,21399],{"alt":21400,"src":21401,"title":21402},"\"Normalizing the payload using change node\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-anlaysis-change-node(1).png","Normalizing the payload using change node",[31,21404,21406],{"id":21405},"installing-custom-node","Installing custom node",[15,21408,21409,21410,21413],{},"Now it’s time to install a custom node that can perform sentiment analysis for us. In this guide, we will use the ",[924,21411,21412],{},"node-red-node-sentiment"," which is a Node-RED node that uses the AFINN-165 wordlists for sentiment analysis of words. It returns a sentiment object containing a score and other properties but we will only use the score property. Score property typically ranges from -5 to 5.",[271,21415,21416,21421,21424],{},[42,21417,1171,21418,21420],{},[924,21419,21412],{}," package by the Node-RED palette manager.",[42,21422,21423],{},"Drag a sentiment node to canvas.",[42,21425,21426],{},"Connect the change nodes output to sentiment node input.",[31,21428,21430],{"id":21429},"calculating-percentage","Calculating percentage",[15,21432,21433],{},"Why do we need to calculate the percentage? We will show the final result with the help of a circular progress bar and three different emojis. Ideally we should show the progress bar based on a percentage of score instead of negative values.",[271,21435,21436,21439],{},[42,21437,21438],{},"Drag another change node to canvas.",[42,21440,21441,21442,1242,21444,21447],{},"set ",[924,21443,1227],{},[924,21445,21446],{},"((msg.sentiment.score - (-5)) \u002F (5 - (-5))) * 100"," as a JSONata expression, it will calculate the percentage of the score.",[15,21449,21450],{},[165,21451],{"alt":21452,"src":21453,"title":21454},"\"Calculating the percentage based on the score using the change node\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-change-node(2).png","Calculating the percentage based on the score using the change node",[31,21456,21458],{"id":21457},"displaying-result-on-dashboard-20","Displaying result on Dashboard 2.0",[15,21460,21461],{},"Finally, we are going to display the result on Dashboard 2.0 with the help of the Vuetify circular progress bar and emojis. To do that we will build a Vue component by using our ui template widget.",[271,21463,21464,21467],{},[42,21465,21466],{},"Drag a ui template widget to canvas and create another group for it.",[42,21468,21469],{},"Paste the below Vue component snippet into the template widget.",[15,21471,21472,21473,21477],{},"We're aware that not everyone coming into Dashboard 2.0 will be familiar with VueJS. We have a more detailed guide ",[63,21474,3986],{"href":21475,"rel":21476},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-template.html#building-full-vue-components",[67],", but we'll also give a quick overview of the component that we'll use to display the result:",[1756,21479,21481],{"className":17125,"code":21480,"language":8779,"meta":103,"style":103},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cv-progress-circular :rotate=\"360\" :size=\"245\" width=\"20\" :width=\"15\" :model-value=\"msg.payload\" color=\"rgb(0,255,0)\">\n      \u003Cimg v-if=\"msg.payload \u003C= 33.33\" src=\"https:\u002F\u002Fi.ibb.co\u002FVHKZ8sn\u002Fimgbin-smirk-emoji-face-emoticon-smile-png.png\" width=\"240\" height=\"240\" alt=\"sad emoji\">\n      \u003Cimg v-else-if=\"msg.payload \u003C= 66.66\" src=\"https:\u002F\u002Fi.ibb.co\u002FnMnybLJ\u002Fimgbin-emoji-computer-icons-emoticon-smiley-png.png\"  width=\"240\" height=\"240\" alt=\"neutral emoji\">\n      \u003Cimg v-else src=\"https:\u002F\u002Fi.ibb.co\u002FTK12RrH\u002FSmile-Emoji-Face-PNG-Download-Image.png\" width=\"240\" height=\"240\" alt=\"happy emoji\">\n    \u003C\u002Fv-progress-circular>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[924,21482,21483,21491,21499,21577,21641,21704,21757,21765,21773],{"__ignoreMap":103},[1107,21484,21485,21487,21489],{"class":1765,"line":1766},[1107,21486,17136],{"class":1783},[1107,21488,17139],{"class":1946},[1107,21490,17142],{"class":1783},[1107,21492,21493,21495,21497],{"class":1765,"line":107},[1107,21494,19835],{"class":1783},[1107,21496,17286],{"class":1946},[1107,21498,17142],{"class":1783},[1107,21500,21501,21503,21506,21509,21511,21513,21516,21518,21521,21523,21525,21528,21530,21532,21534,21536,21539,21541,21544,21546,21548,21551,21553,21556,21558,21560,21562,21564,21566,21568,21570,21573,21575],{"class":1765,"line":294},[1107,21502,19861],{"class":1783},[1107,21504,21505],{"class":1946},"v-progress-circular",[1107,21507,21508],{"class":1775}," :rotate",[1107,21510,1784],{"class":1783},[1107,21512,1970],{"class":1783},[1107,21514,21515],{"class":1966},"360",[1107,21517,1970],{"class":1783},[1107,21519,21520],{"class":1775}," :size",[1107,21522,1784],{"class":1783},[1107,21524,1970],{"class":1783},[1107,21526,21527],{"class":1966},"245",[1107,21529,1970],{"class":1783},[1107,21531,17489],{"class":1775},[1107,21533,1784],{"class":1783},[1107,21535,1970],{"class":1783},[1107,21537,21538],{"class":1966},"20",[1107,21540,1970],{"class":1783},[1107,21542,21543],{"class":1775}," :width",[1107,21545,1784],{"class":1783},[1107,21547,1970],{"class":1783},[1107,21549,21550],{"class":1966},"15",[1107,21552,1970],{"class":1783},[1107,21554,21555],{"class":1775}," :model-value",[1107,21557,1784],{"class":1783},[1107,21559,1970],{"class":1783},[1107,21561,1227],{"class":1966},[1107,21563,1970],{"class":1783},[1107,21565,17388],{"class":1775},[1107,21567,1784],{"class":1783},[1107,21569,1970],{"class":1783},[1107,21571,21572],{"class":1966},"rgb(0,255,0)",[1107,21574,1970],{"class":1783},[1107,21576,17142],{"class":1783},[1107,21578,21579,21581,21583,21585,21587,21589,21592,21594,21597,21599,21601,21604,21606,21608,21610,21612,21615,21617,21620,21622,21624,21626,21628,21630,21632,21634,21637,21639],{"class":1765,"line":104},[1107,21580,19888],{"class":1783},[1107,21582,165],{"class":1946},[1107,21584,17364],{"class":1775},[1107,21586,1784],{"class":1783},[1107,21588,1970],{"class":1783},[1107,21590,21591],{"class":1966},"msg.payload \u003C= 33.33",[1107,21593,1970],{"class":1783},[1107,21595,21596],{"class":1775}," src",[1107,21598,1784],{"class":1783},[1107,21600,1970],{"class":1783},[1107,21602,21603],{"class":1966},"https:\u002F\u002Fi.ibb.co\u002FVHKZ8sn\u002Fimgbin-smirk-emoji-face-emoticon-smile-png.png",[1107,21605,1970],{"class":1783},[1107,21607,17489],{"class":1775},[1107,21609,1784],{"class":1783},[1107,21611,1970],{"class":1783},[1107,21613,21614],{"class":1966},"240",[1107,21616,1970],{"class":1783},[1107,21618,21619],{"class":1775}," height",[1107,21621,1784],{"class":1783},[1107,21623,1970],{"class":1783},[1107,21625,21614],{"class":1966},[1107,21627,1970],{"class":1783},[1107,21629,19893],{"class":1775},[1107,21631,1784],{"class":1783},[1107,21633,1970],{"class":1783},[1107,21635,21636],{"class":1966},"sad emoji",[1107,21638,1970],{"class":1783},[1107,21640,17142],{"class":1783},[1107,21642,21643,21645,21647,21650,21652,21654,21657,21659,21661,21663,21665,21668,21670,21673,21675,21677,21679,21681,21683,21685,21687,21689,21691,21693,21695,21697,21700,21702],{"class":1765,"line":1868},[1107,21644,19888],{"class":1783},[1107,21646,165],{"class":1946},[1107,21648,21649],{"class":1775}," v-else-if",[1107,21651,1784],{"class":1783},[1107,21653,1970],{"class":1783},[1107,21655,21656],{"class":1966},"msg.payload \u003C= 66.66",[1107,21658,1970],{"class":1783},[1107,21660,21596],{"class":1775},[1107,21662,1784],{"class":1783},[1107,21664,1970],{"class":1783},[1107,21666,21667],{"class":1966},"https:\u002F\u002Fi.ibb.co\u002FnMnybLJ\u002Fimgbin-emoji-computer-icons-emoticon-smiley-png.png",[1107,21669,1970],{"class":1783},[1107,21671,21672],{"class":1775},"  width",[1107,21674,1784],{"class":1783},[1107,21676,1970],{"class":1783},[1107,21678,21614],{"class":1966},[1107,21680,1970],{"class":1783},[1107,21682,21619],{"class":1775},[1107,21684,1784],{"class":1783},[1107,21686,1970],{"class":1783},[1107,21688,21614],{"class":1966},[1107,21690,1970],{"class":1783},[1107,21692,19893],{"class":1775},[1107,21694,1784],{"class":1783},[1107,21696,1970],{"class":1783},[1107,21698,21699],{"class":1966},"neutral emoji",[1107,21701,1970],{"class":1783},[1107,21703,17142],{"class":1783},[1107,21705,21706,21708,21710,21713,21715,21717,21719,21722,21724,21726,21728,21730,21732,21734,21736,21738,21740,21742,21744,21746,21748,21750,21753,21755],{"class":1765,"line":1896},[1107,21707,19888],{"class":1783},[1107,21709,165],{"class":1946},[1107,21711,21712],{"class":1775}," v-else",[1107,21714,21596],{"class":1775},[1107,21716,1784],{"class":1783},[1107,21718,1970],{"class":1783},[1107,21720,21721],{"class":1966},"https:\u002F\u002Fi.ibb.co\u002FTK12RrH\u002FSmile-Emoji-Face-PNG-Download-Image.png",[1107,21723,1970],{"class":1783},[1107,21725,17489],{"class":1775},[1107,21727,1784],{"class":1783},[1107,21729,1970],{"class":1783},[1107,21731,21614],{"class":1966},[1107,21733,1970],{"class":1783},[1107,21735,21619],{"class":1775},[1107,21737,1784],{"class":1783},[1107,21739,1970],{"class":1783},[1107,21741,21614],{"class":1966},[1107,21743,1970],{"class":1783},[1107,21745,19893],{"class":1775},[1107,21747,1784],{"class":1783},[1107,21749,1970],{"class":1783},[1107,21751,21752],{"class":1966},"happy emoji",[1107,21754,1970],{"class":1783},[1107,21756,17142],{"class":1783},[1107,21758,21759,21761,21763],{"class":1765,"line":1623},[1107,21760,19931],{"class":1783},[1107,21762,21505],{"class":1946},[1107,21764,17142],{"class":1783},[1107,21766,21767,21769,21771],{"class":1765,"line":1924},[1107,21768,19978],{"class":1783},[1107,21770,17286],{"class":1946},[1107,21772,17142],{"class":1783},[1107,21774,21775,21777,21779],{"class":1765,"line":1930},[1107,21776,17307],{"class":1783},[1107,21778,17139],{"class":1946},[1107,21780,17142],{"class":1783},[39,21782,21783,21790,21796,21808],{},[42,21784,21785,21786,29],{},"v-progress-circular is a Vuetify component to display a circular progress bar, for a detailed guide refer to our blog on  ",[63,21787,21789],{"href":21788},"\u002Fblog\u002F2023\u002F10\u002Fcustom-vuetify-components-dashboard\u002F","Custom Vuetify components for Dashboard 2.0",[42,21791,21792,21795],{},[924,21793,21794],{},"rotate"," is an attribute that lets you specify the rotation angle of the progress bar.",[42,21797,21798,1413,21801,21804,21805,21807],{},[924,21799,21800],{},"size",[924,21802,21803],{},"width"," allow you to set the size of the circular progress bar, and another ",[924,21806,21803],{}," attribute allows you to set the stroke width of the circular progress bar.",[42,21809,21810],{},"v-if, v-else-if, and v-else, allow dynamic rendering of elements based on specified conditions, in this component we are rendering emojis based on percentages calculated by score.",[15,21812,21240],{},[15,21814,21815],{},[165,21816],{"alt":21817,"src":21818,"title":21819},"\"Node-RED flow to do sentiment analysis\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-anlaysis-flow.png","Node-RED flow to do sentiment analysis",[31,21821,14999],{"id":14998},[15,21823,21824],{},[165,21825],{"alt":21254,"src":21826,"title":21256},"\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiement-analysis-flowfuse-editor.png",[15,21828,21829,21830],{},"Finally, we have successfully built our sentiment analysis system. Now it's time to deploy the flow, to do that click on the red deploy button which you can find in the top right corner. After that go to ",[924,21831,18874],{},[15,21833,21834],{},[165,21835],{"alt":21836,"src":21837,"title":21838},"\"Sentiment analysis on Text using Node-RED Dashboard 2.0\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-dashboard-gif.gif","Sentiment analysis on Text using Node-RED Dashboard 2.0",[31,21840,1425],{"id":1424},[15,21842,21843],{},"In this post, a sentiment analysis system is built with Node-RED in which the user has a form field to paste text content. After submitting the form, it calculates the percentage based on the output score, which ranges from -5 to 5. The output will be displayed on dashboard 2.0 by a circular progress bar and three different emojis based on percentage.",[2582,21845,21846],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":21848},[21849,21850,21851,21852,21853,21854,21855,21856],{"id":21316,"depth":107,"text":21317},{"id":21343,"depth":107,"text":21344},{"id":21371,"depth":107,"text":21372},{"id":21405,"depth":107,"text":21406},{"id":21429,"depth":107,"text":21430},{"id":21457,"depth":107,"text":21458},{"id":14998,"depth":107,"text":14999},{"id":1424,"depth":107,"text":1425},"2024-01-23","Learn how to build a sentiment analysis system with Node-RED using Dashboard 2.0. Extract insights from text content effortlessly with step-by-step guidance.",{"excerpt":21860},{"type":12,"value":21861},[21862],[15,21863,21313],{},"\u002Fblog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red",{"title":21307,"description":21858},{"loc":21864},"blog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red","A guide to building a simple sentiment analysis system with Node-RED.",[8650,138,12631,21870,2784],"sentiment analysis","iisqRH6h3R55Z6vCA_dcKaktXIbZwYsuFDZkNumBYv8",{"id":21873,"title":21874,"authors":21875,"body":21876,"cta":3,"date":22002,"description":22003,"extension":116,"image":22004,"lastUpdated":3,"meta":22005,"navigation":129,"path":22010,"seo":22011,"sitemap":22012,"stem":22013,"subtitle":22014,"tags":22015,"tldr":3,"video":3,"__hash__":22016},"blog\u002Fblog\u002F2024\u002F01\u002Fcapture-data-edge-with-node-red-flowfuse.md","Capture Data from edge devices with Node-RED",[2622],{"type":12,"value":21877,"toc":21994},[21878,21881,21884,21888,21891,21894,21898,21905,21911,21917,21924,21928,21935,21941,21949,21953,21959,21962,21975,21981,21984,21988,21991],[15,21879,21880],{},"While cloud computing has revolutionized data access and analysis, not all data can be accessed from the cloud. In many scenarios, data collection from the edge – the location where data is generated – is essential for real-time decision-making or process observability.",[15,21882,21883],{},"FlowFuse enables data to be collected through Node-RED. Data can be processed locally on the edge or sent on to other services. FlowFuse doesn’t rely on continuous connections to the cloud, making it a good choice for locations with unreliable internet connectivity. Use cases like real-time monitoring of critical systems, proactive maintenance, and improved operational efficiency are now possible to implement.",[31,21885,21887],{"id":21886},"installing-the-flowfuse-agent","Installing the FlowFuse agent",[15,21889,21890],{},"To manage the capturing of data on the edge we’re going to first install the FlowFuse agent. It’s installed on your device to manage the communication between the edge device and the FlowFuse server, manage the installation of Node-RED, its execution environment, and facilitate communication between devices and the cloud.",[15,21892,21893],{},"The device agent can run anywhere you can run a Docker container or Node.JS runtime (version 16.0+) can be installed.",[399,21895,21897],{"id":21896},"registering-a-device-on-flowfuse","Registering a device on FlowFuse",[15,21899,21900,21901,21904],{},"For the edge device to know what it’s supposed to do, it needs to listen to the FlowFuse commands. The agent's configuration is provided by a ",[924,21902,21903],{},"device.yml"," file from FlowFuse. Go to the team you’d like to add an edge device to, and select “Devices” on the left-hand menu, followed by the “Add Device” button.",[15,21906,21907],{},[165,21908],{"alt":21909,"src":21910,"title":21909},"Setting up a FlowFuse agent","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fflowfuse-agent-setup.png",[15,21912,21913,21914,21916],{},"FlowFuse will prompt you to add a name (required), and a type (not required). When you’ve clicked ",[924,21915,2449],{}," you’ll get a new dialog to download the required file.",[15,21918,21919],{},[165,21920],{"alt":21921,"src":21922,"title":21923},"Configuration file for the FlowFuse agent","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fdevice-yml-flowfuse.png","The contents of a device.yml file",[399,21925,21927],{"id":21926},"install-the-flowfuse-agent-through-docker","Install the FlowFuse agent through Docker",[15,21929,21930,21931,21934],{},"If your device supports it, the fastest way to run the FlowFuse agent is with containers. Assuming you’ve already got Docker installed, there are two steps to follow: first, move the device YAML file downloaded from FlowFuse to the edge device and save it in ",[924,21932,21933],{},"\u002Fopt\u002Fflowfuse\u002Fdevice.yml",". Start the agent by running:",[1756,21936,21939],{"className":21937,"code":21938,"language":15053},[15051],"docker run --mount type=bind,src=\u002Fpath\u002Fto\u002Fdevice.yml,target=\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml -p 1880:1880 flowfuse\u002Fdevice-agent:latest\n",[924,21940,21938],{"__ignoreMap":103},[15,21942,21943,21944,29],{},"Note that for production cases, ensure the container is restarted on reboot. Docker can do this for you, ",[63,21945,21948],{"href":21946,"rel":21947},"https:\u002F\u002Fdocs.docker.com\u002Fconfig\u002Fcontainers\u002Fstart-containers-automatically\u002F",[67],"please follow their guide",[399,21950,21952],{"id":21951},"install-the-flowfuse-agent-with-npm","Install the FlowFuse agent with npm",[15,21954,21955,21956,29],{},"To install the agent through NPM, you’ll need a Node.JS version of 18.0 or later. Open a command prompt and run: ",[924,21957,21958],{},"npm install -g @flowfuse\u002Fdevice-agent",[15,21960,21961],{},"This will install the FlowFuse Device Agent as a global npm module, making the flowfuse-device-agent command available in any directory on your system.",[15,21963,21964,21965,21967,21968,21971,21972,29],{},"Once the installation is complete, you must configure the Device Agent to connect to your FlowFuse instance. In this guide, you’ve previously downloaded the ",[924,21966,21903],{}," file that’s needed now. On Linux or Mac, move the file to ",[924,21969,21970],{},"\u002Fopt\u002Fflowfuse-device\u002Fdevice.yml",", and for Windows-based systems, move the file to ",[924,21973,21974],{},"c:\\opt\\flowfuse-device\\device.yml",[15,21976,21977,21978,29],{},"Afterward, start the agent with: ",[924,21979,21980],{},"flowfuse-device-agent",[15,21982,21983],{},"This will launch the Device Agent and connect it to your FlowFuse instance. The Device Agent will wait for instructions on which flows to run.",[399,21985,21987],{"id":21986},"programming-flows-for-the-edge","Programming flows for the edge",[15,21989,21990],{},"Now the agent is running, the FlowFuse platform will show it has contacted back to the platform and is ready to do some work. First, add it to the application and start the developer mode. That enables the device editor and provides you secure access to the editor anywhere in the world for everyone in the FlowFuse team with the right access role.",[15,21992,21993],{},"When the development is done, be sure to create a snapshot of the developed flows to create a point-in-time backup, or to roll the snapshot out to many other devices later.",{"title":103,"searchDepth":104,"depth":104,"links":21995},[21996],{"id":21886,"depth":107,"text":21887,"children":21997},[21998,21999,22000,22001],{"id":21896,"depth":294,"text":21897},{"id":21926,"depth":294,"text":21927},{"id":21951,"depth":294,"text":21952},{"id":21986,"depth":294,"text":21987},"2024-01-17","Learn to capture data from edge devices with Node-RED using FlowFuse. Install the agent via Docker or npm, configure device registration, and program custom flows.","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fcapture-data-from-edge-devices.png",{"excerpt":22006},{"type":12,"value":22007},[22008],[15,22009,21880],{},"\u002Fblog\u002F2024\u002F01\u002Fcapture-data-edge-with-node-red-flowfuse",{"title":21874,"description":22003},{"loc":22010},"blog\u002F2024\u002F01\u002Fcapture-data-edge-with-node-red-flowfuse","FlowFuse allows you to run Node-RED anywhere to capture all the data",[8650,10,138,7457],"-Z2KwRVeMk4C6N8s6yJ8a0HC84jUEVvkcQygW9X4Sq8",{"id":22018,"title":22019,"authors":22020,"body":22022,"cta":3,"date":22223,"description":22224,"extension":116,"image":22225,"lastUpdated":3,"meta":22226,"navigation":129,"path":22234,"seo":22235,"sitemap":22236,"stem":22237,"subtitle":22238,"tags":22239,"tldr":3,"video":3,"__hash__":22241},"blog\u002Fblog\u002F2023\u002F11\u002Fdevice-agent-balena.md","Deploying the FlowFuse Device Agent via Balena",[22021],"ben-hardill",{"type":12,"value":22023,"toc":22217},[22024,22033,22036,22039,22048,22052,22059,22112,22121,22140,22150,22154,22157,22171,22175,22181,22202,22208,22214],[15,22025,22026,22027,22032],{},"As part of the FlowFuse Staff Summit this year in Barcelona we met up with Marc Pous from ",[63,22028,22031],{"href":22029,"rel":22030},"https:\u002F\u002Fwww.balena.io\u002F",[67],"Balena Io",". Balena is a platform for managing fleets of Edge Devices and it felt like the perfect fit for deploying the FlowFuse Device Agent.",[15,22034,22035],{},"To do this you install the Balena OS on the devices, this is a stripped down Linux distribution that includes a client that connects back to Balena's platform and creates a VPN tunnel. As well as the Balena client it includes Docker and users can select containers to push to the devices.",[15,22037,22038],{},"These Docker container are hosted on Balena's own container registry and are built by doing a git push to Balena's git server.",[15,22040,22041,22042,22047],{},"A GitHub repository with all the required files ",[63,22043,22046],{"href":22044,"rel":22045},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fbalena-device-agent",[67],"has been published",", a one click deploy button to allow you to quickly try this out.",[31,22049,22051],{"id":22050},"building-flowfuse-device-agent-for-balena","Building FlowFuse Device Agent for Balena",[15,22053,22054,22055,22058],{},"We already build a FlowFuse Device Agent Docker container so it was pretty simple to modify the existing ",[924,22056,22057],{},"Dockerfile"," for Balena.",[1756,22060,22064],{"className":22061,"code":22062,"language":22063,"meta":103,"style":103},"language-docker shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM balenalib\u002F%%BALENA_MACHINE_NAME%%-alpine-node\n\nRUN mkdir \u002Fopt\u002Fflowfuse-device\nRUN npm install -g @flowfuse\u002Fdevice-agent\n\nCOPY entrypoint.sh \u002Fusr\u002Fsrc\u002Fentrypoint.sh\n\nENTRYPOINT [\"\u002Fusr\u002Fsrc\u002Fentrypoint.sh\"]\n\nCMD [\"flowfuse-device-agent\"]\n","docker",[924,22065,22066,22071,22075,22080,22085,22089,22094,22098,22103,22107],{"__ignoreMap":103},[1107,22067,22068],{"class":1765,"line":1766},[1107,22069,22070],{},"FROM balenalib\u002F%%BALENA_MACHINE_NAME%%-alpine-node\n",[1107,22072,22073],{"class":1765,"line":107},[1107,22074,1921],{"emptyLinePlaceholder":129},[1107,22076,22077],{"class":1765,"line":294},[1107,22078,22079],{},"RUN mkdir \u002Fopt\u002Fflowfuse-device\n",[1107,22081,22082],{"class":1765,"line":104},[1107,22083,22084],{},"RUN npm install -g @flowfuse\u002Fdevice-agent\n",[1107,22086,22087],{"class":1765,"line":1868},[1107,22088,1921],{"emptyLinePlaceholder":129},[1107,22090,22091],{"class":1765,"line":1896},[1107,22092,22093],{},"COPY entrypoint.sh \u002Fusr\u002Fsrc\u002Fentrypoint.sh\n",[1107,22095,22096],{"class":1765,"line":1623},[1107,22097,1921],{"emptyLinePlaceholder":129},[1107,22099,22100],{"class":1765,"line":1924},[1107,22101,22102],{},"ENTRYPOINT [\"\u002Fusr\u002Fsrc\u002Fentrypoint.sh\"]\n",[1107,22104,22105],{"class":1765,"line":1930},[1107,22106,1921],{"emptyLinePlaceholder":129},[1107,22108,22109],{"class":1765,"line":1940},[1107,22110,22111],{},"CMD [\"flowfuse-device-agent\"]\n",[15,22113,22114,22115],{},"There were 2 main changes from the default Device Agent ",[63,22116,22119],{"href":22117,"rel":22118},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fdevice-agent\u002Fblob\u002Fmain\u002Fdocker\u002FDockerfile",[67],[924,22120,22057],{},[271,22122,22123,22129],{},[42,22124,22125,22126,22128],{},"Change the base image to Balena's image, this is because the ",[924,22127,22057],{}," is actually a template that can be used to build images optimized for all Balena's supported hardware platforms (We currently build the FlowFuse Device Agent containers for AMD64, ARMv7 and ARM64)",[42,22130,22131,22132,22135,22136,8879],{},"Adding a custom ",[924,22133,22134],{},"entrypoint.sh",". This is to ensure that the hostname seen in the container matches the Balena device name, making it easier to match it up with what is seen in the FlowFuse application. It also generates the configuration file from the passed in environment variable (see ",[63,22137,22139],{"href":22138},"#configuring-devices","below",[15,22141,22142,22143,22145,22146,22149],{},"As well as the ",[924,22144,22057],{}," there is also a ",[924,22147,22148],{},"docker-compose.yml"," because Balena applications can be made up of multiple services packaged as container. In this case we just need a single container but the compose file contains all the information about what ports to expose and what volumes need creating to persist state.",[31,22151,22153],{"id":22152},"configuring-devices","Configuring Devices",[15,22155,22156],{},"The FlowFuse Device agent can be configured in 2 ways.",[271,22158,22159,22165],{},[42,22160,22161,22162,29],{},"You can provide a configuration file that is provided by the FlowFuse application when you create a new Device. This file contains the unique identifiers for the Device and details of where to find the FlowFuse Application. This file can be provided to a Balena device by adding a device specific environment variable as described ",[63,22163,22139],{"href":22164},"#environment-variable",[42,22166,22167,22168,22170],{},"You can provide a fleet of devices with a configuration file that contains details of where to find the FlowFuse application and a Provisioning token. Multiple Devices can all have the same Provisioning token and this will cause them to connect to the FlowFuse application on first start up and create a new Device bound to an existing team (and optionally an Application or Instance). This file can be passed to Balena devices by way of a Fleet wide environment variable as described ",[63,22169,22139],{"href":22164},".\nYou can create a Provisioning Token file under the Team -> Settings page on the Devices tab.",[399,22172,22174],{"id":22173},"environment-variable","Environment Variable",[15,22176,22177,22178,22180],{},"Because the ",[924,22179,21903],{}," file is multi line it needs to be base64 encoded, you can do this with the following",[1756,22182,22184],{"className":13415,"code":22183,"language":13417,"meta":103,"style":103},"$ base64 -w 0 device.yml\n",[924,22185,22186],{"__ignoreMap":103},[1107,22187,22188,22191,22194,22197,22199],{"class":1765,"line":1766},[1107,22189,22190],{"class":10543},"$",[1107,22192,22193],{"class":1966}," base64",[1107,22195,22196],{"class":1966}," -w",[1107,22198,15738],{"class":1854},[1107,22200,22201],{"class":1966}," device.yml\n",[15,22203,22204,22205,29],{},"You can then use the Balena console to create either a device specific or a fleet wide environment variable called ",[924,22206,22207],{},"FF_DEVICE_YML",[15,22209,22210],{},[165,22211],{"alt":22212,"src":22213},"balena-env-var","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fbalena-env-var.png",[2582,22215,22216],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":103,"searchDepth":104,"depth":104,"links":22218},[22219,22220],{"id":22050,"depth":107,"text":22051},{"id":22152,"depth":107,"text":22153,"children":22221},[22222],{"id":22173,"depth":294,"text":22174},"2023-11-24","Learn how to deploy the FlowFuse Device Agent via Balena.io for efficient fleet management. Utilize Docker files and environment variables to configure devices seamlessly.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fbalena.png",{"excerpt":22227},{"type":12,"value":22228},[22229],[15,22230,22026,22231,22032],{},[63,22232,22031],{"href":22029,"rel":22233},[67],"\u002Fblog\u002F2023\u002F11\u002Fdevice-agent-balena",{"title":22019,"description":22224},{"loc":22234},"blog\u002F2023\u002F11\u002Fdevice-agent-balena","Using Balena.io to Deploy fleets of devices",[138,10789,22240],"balena","UocXqBMi_eZfo7XUG8q0Vcg4I0Ko7cqbAiIbIHKdBpA",{"id":22243,"title":22244,"authors":22245,"body":22246,"cta":3,"date":22342,"description":22343,"extension":116,"image":22344,"lastUpdated":3,"meta":22345,"navigation":129,"path":22354,"seo":22355,"sitemap":22356,"stem":22357,"subtitle":22358,"tags":22359,"tldr":3,"video":3,"__hash__":22361},"blog\u002Fblog\u002F2023\u002F11\u002Fai-assistant.md","Integrate with ChatGPT Assistants with Node-RED",[18165],{"type":12,"value":22247,"toc":22336},[22248,22252,22255,22258,22265,22269,22272,22276,22279,22285,22317,22324,22328,22331],[31,22249,22251],{"id":22250},"introduction-to-the-world-of-gpts-and-ai-assistants","Introduction to the World of GPTs and AI Assistants",[15,22253,22254],{},"In the ever-evolving landscape of artificial intelligence, Generative Pre-trained Transformers (GPTs) have emerged as groundbreaking tools. These advanced AI models, developed by OpenAI, are capable of understanding and generating human-like text, offering vast possibilities across numerous applications. GPTs learn from various internet texts, enabling them to respond to queries with human-like understanding.",[15,22256,22257],{},"Among the most intriguing developments in this field are AI Assistants. These are specialized applications of GPTs, accessible through an API, designed to enhance and streamline various tasks. Tasks that include code interpreter, functions, retrieval, and leveraging uploading files to interact with. Unlike traditional GPTs, which primarily focus on generating text, AI Assistants can interact, comprehend, and assist in real-time, making them invaluable in industries ranging from manufacturing to finance to healthcare.",[15,22259,22260],{},[63,22261,22264],{"href":22262,"rel":22263},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F073548c276832e804f037f3212014e60",[67],"TLDR: Give me the Flows",[31,22266,22268],{"id":22267},"node-red-and-ai-assistants","Node-RED and AI Assistants",[15,22270,22271],{},"The integration of Node-RED with AI Assistants brings a unique set of advantages. By leveraging Node-RED's user-friendly platform, developers and citizen developers can easily harness the power of AI Assistants. This integration allows for creation of bespoke solutions tailored to specific industry needs, ranging from automated customer service to advanced data analytics. The real-world impact is substantial – imagine a manufacturing line where real-time data is seamlessly integrated with a prescriptive AI-driven decision-making prompt, enhancing efficiency and reducing downtime.  In healthcare, it provides patients with real-time updates to their personal data and provides contextual information, while in retail, it could enhance customer engagement through personalized interactions. The future shaped by these technologies is one where automation and intelligence converge, leading to unprecedented levels of efficiency and innovation in various sectors.",[31,22273,22275],{"id":22274},"experience-the-integration-firsthand","Experience the Integration Firsthand",[15,22277,22278],{},"We invite you to explore the possibilities firsthand. Try out the flows we've created and share your feedback. This is your getting started package. In the provided flows, you can do the following:",[15,22280,22281],{},[165,22282],{"alt":22283,"src":22284},"OpenAI Assistant integration on Node-RED","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fai-flows.png",[271,22286,22287,22293,22299,22305,22311],{},[42,22288,22289,22292],{},[19,22290,22291],{},"Create Assistant",": This flow creates a new assistant. It starts with an inject node that sets the assistant's name, instructions, tools, and model. The HTTP request node then sends a POST request to the OpenAI API to create the assistant. The assistant's ID is stored in the flow context for later use.",[42,22294,22295,22298],{},[19,22296,22297],{},"List Assistants",": This flow lists all the assistants that have been created. It starts with an inject node that triggers the flow. The HTTP request node sends a GET request to the OpenAI API to retrieve the list of assistants. The results are then displayed in the debug node.",[42,22300,22301,22304],{},[19,22302,22303],{},"Delete Assistant",": This flow deletes an assistant. It starts with an inject node that sets the assistant's ID. The template node constructs the URL for the HTTP request node, which sends a DELETE request to the OpenAI API to delete the assistant. The results are then displayed in the debug node.",[42,22306,22307,22310],{},[19,22308,22309],{},"Adjust Assistant Instructions and Models",": This flow adjusts the instructions and model of an assistant. It starts with an inject node that sets the assistant's ID, new instructions, and new model. The change node prepares the payload for the HTTP request node, which sends a POST request to the OpenAI API to update the assistant. The results are then displayed in the debug node.",[42,22312,22313,22316],{},[19,22314,22315],{},"Create Thread and Run",": This flow creates a new thread and runs it. It starts with an inject node that sets the assistant's ID and the message to be sent. The subflow node then handles the creation of the thread, sending of the message, and retrieval of the response. The results are then displayed in the debug node.",[15,22318,22319,22320],{},"How do you envision leveraging this integration in your day-to-day operations or within your industry? Your insights are valuable in shaping the future of our industry. Begin your journey ",[63,22321,22323],{"href":22262,"rel":22322},[67],"here.",[31,22325,22327],{"id":22326},"embracing-the-future-of-ai-and-automation","Embracing the Future of AI and Automation",[15,22329,22330],{},"Integrating Node-RED with OpenAI's Assistants is a testament to the ever-evolving landscape of technology. It represents a step towards a future where powerful AI tools are within reach of a wider audience, enabling the creation of bespoke, flexible, and resilient applications across industries. By embracing this integration, we open doors to innovation and efficiency previously unimagined.",[15,22332,22333],{},[170,22334,22335],{},"Always consult with management before uploading company data to public services like ChatGPT.",{"title":103,"searchDepth":104,"depth":104,"links":22337},[22338,22339,22340,22341],{"id":22250,"depth":107,"text":22251},{"id":22267,"depth":107,"text":22268},{"id":22274,"depth":107,"text":22275},{"id":22326,"depth":107,"text":22327},"2023-11-21","Discover how seamlessly integrating AI Assistants into Node-RED workflows enhances efficiency and innovation across industries.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fai-assistant.png",{"excerpt":22346},{"type":12,"value":22347},[22348,22350,22352],[31,22349,22251],{"id":22250},[15,22351,22254],{},[15,22353,22257],{},"\u002Fblog\u002F2023\u002F11\u002Fai-assistant",{"title":22244,"description":22343},{"loc":22354},"blog\u002F2023\u002F11\u002Fai-assistant","Get start quickly leveraging Flows utilizing ChatGPT Assistant",[8650,138,137,10,22360,2784],"openai","6z539bKI9937c4G2WhhRG53ZA0efw9mhmIe1y4xquiw",{"id":22363,"title":22364,"authors":22365,"body":22366,"cta":3,"date":22389,"description":22390,"extension":116,"image":22391,"lastUpdated":3,"meta":22392,"navigation":129,"path":22397,"seo":22398,"sitemap":22399,"stem":22400,"subtitle":22401,"tags":22402,"tldr":3,"video":3,"__hash__":22404},"blog\u002Fblog\u002F2023\u002F11\u002Fchatgpt-gpt.md","Node-RED Builder a GPT (Alpha) by FlowFuse",[18165],{"type":12,"value":22367,"toc":22387},[22368,22371,22374,22377,22380],[15,22369,22370],{},"When ChatGPT was first released, my expectations were quite low. I had grown accustomed to the usual industry buzz for AI and ML that often led to underwhelming solutions. Naturally, I approached ChatGPT with similar reservations. It wasn't until a few months after its announcement that I decided to give it a try. To my surprise, within just 10 minutes, I found myself so captivated that I decided to purchase the pro version.",[15,22372,22373],{},"On November 6th, OpenAI unveiled a new offering: GPTs. These function as custom ChatGPT environments, allowing the author to provide additional context, giving it a specific and focused purpose. With new content emerging daily for both Node-RED and FlowFuse, the ability to update and provide essential documentation became increasingly valuable.",[15,22375,22376],{},"ChatGPT is already a fantastic tool for building Node-RED flows, and if you haven't tried it yet, I highly recommend giving it a go. Now, let me introduce you to Node-RED Builder, a preconfigured environment where all the necessary prompts are already set up to ensure your success. Furthermore, the latest knowledge on Node-RED and FlowFuse is readily available within the GPT, allowing you to tap into the most up-to-date documentation for your prompts.",[15,22378,22379],{},"Node-RED Builder streamlines the development of Node-RED flows, making it more accessible, especially for those new to this environment. We've even provided context to emphasize the use of default nodes over function nodes. Imagine being able to simply drag and drop elements, connect nodes, and create functional flows without delving deep into complex coding. This is precisely what Node-RED Builder makes easier, effectively opening the doors of Node-RED to a wider audience.",[15,22381,22382],{},[63,22383,22386],{"href":22384,"rel":22385},"https:\u002F\u002Fchat.openai.com\u002Fg\u002Fg-V5Kyn4omE-node-red-builder-by-flowfuse-v1-0-2",[67],"Access to the GPT - Node-RED builder by FlowFuse",{"title":103,"searchDepth":104,"depth":104,"links":22388},[],"2023-11-15","Accelerate Node-RED flow creation with Node-RED Builder by FlowFuse. Streamline development effortlessly with preconfigured prompts and latest Node-RED insights.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fchatgpt-GPT.png",{"excerpt":22393},{"type":12,"value":22394},[22395],[15,22396,22370],{},"\u002Fblog\u002F2023\u002F11\u002Fchatgpt-gpt",{"title":22364,"description":22390},{"loc":22397},"blog\u002F2023\u002F11\u002Fchatgpt-gpt","Speed Up Flow Creation with Your Personal Assistant",[8650,138,137,10,22403,2784],"chatgpt","auW-Gn2MhZGEvyPaPdei22kwPY98YZ3KNDJhc8KK06A",{"id":22406,"title":22407,"authors":22408,"body":22409,"cta":3,"date":22552,"description":22553,"extension":116,"image":22554,"lastUpdated":3,"meta":22555,"navigation":129,"path":22560,"seo":22561,"sitemap":22562,"stem":22563,"subtitle":22564,"tags":22565,"tldr":3,"video":3,"__hash__":22566},"blog\u002Fblog\u002F2023\u002F11\u002Fdashboard-2.0-user-tracking.md","Tracking Who Has Opened a Dashboard",[22021],{"type":12,"value":22410,"toc":22547},[22411,22414,22418,22421,22424,22427,22430,22433,22437,22440,22446,22449,22467,22470,22535,22538,22542,22545],[15,22412,22413],{},"As we continue to add features to the Node-RED Dashboard v2 one feature request that came in was to track which users had visited a Dashboard. Multi user support for the Dashboard is on the backlog but this could be solved with the parts that are currently available.",[31,22415,22417],{"id":22416},"flowfuse-authentication","FlowFuse Authentication",[15,22419,22420],{},"One of the features we offer on FlowFuse is the ability to protect HTTP endpoints and Dashboards using the same FlowFuse user authentication that protects access to the FlowFuse Application and the Node-RED instances.",[15,22422,22423],{},"We even offer a specific RBAC 'viewer' role that just allows access to these endpoints but not the FlowFuse application.",[15,22425,22426],{},"FlowFuse authentication can be enabled from the Instance Settings page on the Security tab",[15,22428,22429],{},"This can be used to secure access to a Dashboard hosted in a Node-RED Instance. At the moment the Dashboard while protected by this authentication, it is not aware of which user is accessing it.",[15,22431,22432],{},"But if we include an element in the Dashboard loaded via a HTTP-in\u002FHTTP-response node we gain access to details of the authenticated user.",[31,22434,22436],{"id":22435},"implementation","Implementation",[15,22438,22439],{},"First we will create a HTTP-in\u002FHTTP-response pair to serve up a single pixel SVG image. I chose SVG as it doesn't require creating a binary image file to load.",[15,22441,22442,22443,22445],{},"The following flow snippet includes both the HTTP-in\u002FHTTP-response nodes and a change node to set the ",[924,22444,1227],{}," to the SVG content and to set the HTTP headers to include the correct mime type.",[15,22447,22448],{},"There is also a second change node which extracts the user information.",[15,22450,4917,22451,1150],{},[1107,22452,22453,22454,22456,22457,22460,22461,22464,22465,4957],{},"{\"id\":\"7f22dc81d8192d4d\",\"type\":\"http in\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"url\":\"\u002Ftracker\",\"method\":\"get\",\"upload\":false,\"swaggerDoc\":\"\",\"x\":210,\"y\":460,\"wires\":[[\"7d36739c02cd04ec\",\"5f4647c97917cce1\"]]},{\"id\":\"58fd30516a077e29\",\"type\":\"http response\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"statusCode\":\"\",\"headers\":{},\"x\":630,\"y\":460,\"wires\":",[1107,22455],{},"},{\"id\":\"7d36739c02cd04ec\",\"type\":\"change\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"rules\":",[1107,22458,22459],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"\u003Csvg width=\"1\" height=\"1\"> \u003Crect width=\"1\" height=\"1\" style=\"fill:rgb(255,255,255);stroke-width:3;stroke:rgb(0,0,0)\" \u002F> Sorry, your browser does not support inline SVG.\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"headers\",\"pt\":\"msg\",\"to\":\"{\"Content-Type\":\"image\u002Fsvg+xml\"}\",\"tot\":\"json\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":420,\"y\":460,\"wires\":[[\"58fd30516a077e29\"]]},{\"id\":\"5f4647c97917cce1\",\"type\":\"change\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"rules\":",[1107,22462,22463],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"req.session.user\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":420,\"y\":520,\"wires\":[[\"ddc02b4e9c30c807\"]]},{\"id\":\"ddc02b4e9c30c807\",\"type\":\"debug\",\"z\":\"98c8d7ea66149291\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":600,\"y\":520,\"wires\":",[1107,22466],{},[15,22468,22469],{},"Next we need to add the SVG to the Dashboard, this can be done by adding a Template node with the following HTML content.",[1756,22471,22473],{"className":17125,"code":22472,"language":8779,"meta":103,"style":103},"\u003Cdiv>\n    \u003Cobject data=\"\u002Ftracker\" height=\"1\" width=\"1\">\u003C\u002Fobject>\n\u003C\u002Fdiv>\n",[924,22474,22475,22483,22527],{"__ignoreMap":103},[1107,22476,22477,22479,22481],{"class":1765,"line":1766},[1107,22478,17136],{"class":1783},[1107,22480,17286],{"class":1946},[1107,22482,17142],{"class":1783},[1107,22484,22485,22487,22490,22492,22494,22496,22499,22501,22503,22505,22507,22509,22511,22513,22515,22517,22519,22521,22523,22525],{"class":1765,"line":107},[1107,22486,19861],{"class":1783},[1107,22488,22489],{"class":1946},"object",[1107,22491,20023],{"class":1775},[1107,22493,1784],{"class":1783},[1107,22495,1970],{"class":1783},[1107,22497,22498],{"class":1966},"\u002Ftracker",[1107,22500,1970],{"class":1783},[1107,22502,21619],{"class":1775},[1107,22504,1784],{"class":1783},[1107,22506,1970],{"class":1783},[1107,22508,4944],{"class":1966},[1107,22510,1970],{"class":1783},[1107,22512,17489],{"class":1775},[1107,22514,1784],{"class":1783},[1107,22516,1970],{"class":1783},[1107,22518,4944],{"class":1966},[1107,22520,1970],{"class":1783},[1107,22522,17215],{"class":1783},[1107,22524,22489],{"class":1946},[1107,22526,17142],{"class":1783},[1107,22528,22529,22531,22533],{"class":1765,"line":294},[1107,22530,17307],{"class":1783},[1107,22532,17286],{"class":1946},[1107,22534,17142],{"class":1783},[15,22536,22537],{},"This will load the image every time the Dashboard page loads and hence trigger the earlier flow allowing the user to be logged.",[31,22539,22541],{"id":22540},"linking-to-sso-users","Linking to SSO Users",[15,22543,22544],{},"With the release of FlowFuse v1.14.0 the session object will also include the users email address which is the shared identifier between FlowFuse and the SSO system. This will allow the logging to use a single unified identifier.",[2582,22546,21846],{},{"title":103,"searchDepth":104,"depth":104,"links":22548},[22549,22550,22551],{"id":22416,"depth":107,"text":22417},{"id":22435,"depth":107,"text":22436},{"id":22540,"depth":107,"text":22541},"2023-11-13","Learn how to track user visits to your Node-RED Dashboard v2 using FlowFuse Authentication. Secure access and identify users accessing your dashboard with this implementation.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fdashboard-user-flow.png",{"excerpt":22556},{"type":12,"value":22557},[22558],[15,22559,22413],{},"\u002Fblog\u002F2023\u002F11\u002Fdashboard-2.0-user-tracking",{"title":22407,"description":22553},{"loc":22560},"blog\u002F2023\u002F11\u002Fdashboard-2.0-user-tracking","Using FlowFuse Authentication Audit Dashboard v2 Access",[138,12631,10],"RA1SYFFb9kjmMqpG73yW9koy71uD353RY5q5kEj8RG8",{"id":22568,"title":22569,"authors":22570,"body":22571,"cta":3,"date":22629,"description":22630,"extension":116,"image":22631,"lastUpdated":3,"meta":22632,"navigation":129,"path":22637,"seo":22638,"sitemap":22639,"stem":22640,"subtitle":22641,"tags":22642,"tldr":3,"video":3,"__hash__":22643},"blog\u002Fblog\u002F2023\u002F10\u002Fmes-build-buy.md","Embracing Innovation: Build vs Buy in MES",[18165],{"type":12,"value":22572,"toc":22621},[22573,22576,22580,22583,22587,22590,22594,22597,22601,22604,22608,22611,22615,22618],[15,22574,22575],{},"Manufacturing companies often struggle to choose between building custom MES solutions or buying ready-made software. Both options have their strengths, but they can fall short of providing the flexibility and control engineers need. A hybrid approach, combining the best of both worlds, is changing the game. In this article, we’ll explore how Node-RED and FlowFuse make it easier to create MES systems that are efficient, adaptable, and perfectly suited to your needs.",[31,22577,22579],{"id":22578},"the-hybrid-approach-a-best-of-both-worlds-strategy","The Hybrid Approach: A Best of Both Worlds Strategy",[15,22581,22582],{},"Much like the practice of outsourcing machine installation while specifying detailed requirements, a hybrid approach to MES systems leverages the expertise of SI companies while retaining control over critical aspects of system design. This approach recognizes that automation engineers possess valuable insights into the unique needs of their manufacturing processes. They can specify crucial details such as wire coloring schemes, downtime conventions, Andon board standards, PLC types, and even the code used on PLCs.",[31,22584,22586],{"id":22585},"node-red-the-mes-codes-best-friend","Node-RED: The MES Code's Best Friend",[15,22588,22589],{},"Node-RED emerges as a game-changer in this paradigm. It is an open-source, flow-based development tool renowned for its ability to connect everything, from PLCs to relational databases to firewalls, empowering engineers to create MES system logic visually. Node-RED simplifies the process by enabling engineers to drag and drop nodes and wires to define the logic flow. This visual approach not only streamlines development but also enhances the transparency of the codebase.",[31,22591,22593],{"id":22592},"the-power-of-standardization-and-code-clarity","The Power of Standardization and Code Clarity",[15,22595,22596],{},"One of the primary reasons behind Node-RED's appeal is its ability to standardize code. Just as Ladder Logic and Function Block Diagrams have long been favored for their clarity, Node-RED fosters a coding environment where engineers can easily understand and build upon each other's work. This standardization ensures that MES systems remain in sync with the rest of the plant, making troubleshooting and maintenance more efficient.",[31,22598,22600],{"id":22599},"flowfuse-bridging-the-gap-to-deployment","FlowFuse: Bridging the Gap to Deployment",[15,22602,22603],{},"When it comes to deploying MES systems, FlowFuse enters the scene as a vital companion to Node-RED. FlowFuse is a deployment platform that seamlessly integrates with Node-RED, allowing for effortless deployment of applications to a customer's environment. Its user-friendly interface makes it easy for automation engineers to manage and scale their MES systems.",[31,22605,22607],{"id":22606},"pioneering-the-future-the-strategic-adoption-of-node-red-and-flowfuse","Pioneering the Future: The Strategic Adoption of Node-RED and FlowFuse",[15,22609,22610],{},"Embracing Node-RED and FlowFuse in MES system development is not just a technical choice; it's a strategic one. By adopting this hybrid approach, manufacturing companies position themselves as thought leaders in the industry. They demonstrate a commitment to innovation, transparency, and adaptability.",[31,22612,22614],{"id":22613},"elevate-your-mes-strategy-embrace-node-red-and-flowfuse-today","Elevate Your MES Strategy: Embrace Node-RED and FlowFuse Today",[15,22616,22617],{},"In conclusion, the answer to the build vs. buy dilemma for MES systems is not one or the other, it's both. Node-RED and FlowFuse offer a dynamic partnership that empowers manufacturing companies to craft MES solutions that are tailored to their needs while harnessing the expertise of System Integration companies. The call to action is clear: Embrace Node-RED and FlowFuse as the catalysts for your manufacturing innovation journey.",[15,22619,22620],{},"For more information about FlowFuse and how it can revolutionize your MES system deployment, visit FlowFuse.com Start building MES systems that are not just efficient but also future-ready, and become a trailblazer in the world of industrial manufacturing.",{"title":103,"searchDepth":104,"depth":104,"links":22622},[22623,22624,22625,22626,22627,22628],{"id":22578,"depth":107,"text":22579},{"id":22585,"depth":107,"text":22586},{"id":22592,"depth":107,"text":22593},{"id":22599,"depth":107,"text":22600},{"id":22606,"depth":107,"text":22607},{"id":22613,"depth":107,"text":22614},"2023-10-20","Transform MES strategy with Node-RED and FlowFuse for unparalleled efficiency and adaptability.","\u002Fblog\u002F2023\u002F10\u002Fimages\u002Fbuildvbuy.png",{"excerpt":22633},{"type":12,"value":22634},[22635],[15,22636,22575],{},"\u002Fblog\u002F2023\u002F10\u002Fmes-build-buy",{"title":22569,"description":22630},{"loc":22637},"blog\u002F2023\u002F10\u002Fmes-build-buy","Bridging the Gap: Uniting MES Development with Automation System Practices",[8650,138,137,10],"sGN3wxlQc2XbSaVFPCvCvyoCrm-lQLQADphkhtd7Qag",{"id":22645,"title":21789,"authors":22646,"body":22647,"cta":3,"date":22920,"description":22921,"extension":116,"image":22922,"lastUpdated":3,"meta":22923,"navigation":129,"path":22930,"seo":22931,"sitemap":22932,"stem":22933,"subtitle":22921,"tags":22934,"tldr":3,"video":3,"__hash__":22935},"blog\u002Fblog\u002F2023\u002F10\u002Fcustom-vuetify-components-dashboard.md",[2622],{"type":12,"value":22648,"toc":22912},[22649,22656,22664,22668,22681,22684,22734,22738,22752,22759,22763,22772,22782,22785,22832,22842,22857,22864,22868,22871,22885,22888,22910],[15,22650,22651,22652,22655],{},"Vuetify is a library of UI components using Vue. This saves the developers of\nDashboard 2.0 a lot of time, but it can also help you, the end-user. As Vuetify\nis now included, it can be used to include ",[170,22653,22654],{},"any"," of their components. So in this\npost we're going to use a few of these to teach you how to use any of them.",[15,22657,22658,22659,22663],{},"Let's install the ",[63,22660,22662],{"href":13959,"rel":22661},[67],"Dashboard 2.0 package"," if you want to follow along. When that's done, let's figure\nout how to build custom components on dashboards.",[31,22665,22667],{"id":22666},"custom-components","Custom components",[15,22669,22670,22671,22674,22675,22680],{},"While going through the list of components on ",[63,22672,13859],{"href":17087,"rel":22673},[67],"\nthere's several examples that aren't natively implemented in Dashboard 2.0.\nOne example we'll use in a dashboard in this post is the\n",[63,22676,22679],{"href":22677,"rel":22678},"https:\u002F\u002Fvuetifyjs.com\u002Fen\u002Fcomponents\u002Fprogress-circular\u002F",[67],"Progress circular"," to\nbuild a count down timer.",[15,22682,22683],{},"The documentation explains which elements one can change, in this case the size and\nwidth. Having set those to the values you'd want in your dashboard, the HTML is\ngenerated for you, in my case it's:",[1756,22685,22687],{"className":17125,"code":22686,"language":8779,"meta":103,"style":103},"\u003Cv-progress-circular model-value=\"20\" :size=\"128\" :width=\"12\">\u003C\u002Fv-progress-circular>\n",[924,22688,22689],{"__ignoreMap":103},[1107,22690,22691,22693,22695,22698,22700,22702,22704,22706,22708,22710,22712,22715,22717,22719,22721,22723,22726,22728,22730,22732],{"class":1765,"line":1766},[1107,22692,17136],{"class":1783},[1107,22694,21505],{"class":1946},[1107,22696,22697],{"class":1775}," model-value",[1107,22699,1784],{"class":1783},[1107,22701,1970],{"class":1783},[1107,22703,21538],{"class":1966},[1107,22705,1970],{"class":1783},[1107,22707,21520],{"class":1775},[1107,22709,1784],{"class":1783},[1107,22711,1970],{"class":1783},[1107,22713,22714],{"class":1966},"128",[1107,22716,1970],{"class":1783},[1107,22718,21543],{"class":1775},[1107,22720,1784],{"class":1783},[1107,22722,1970],{"class":1783},[1107,22724,22725],{"class":1966},"12",[1107,22727,1970],{"class":1783},[1107,22729,17215],{"class":1783},[1107,22731,21505],{"class":1946},[1107,22733,17142],{"class":1783},[399,22735,22737],{"id":22736},"using-the-template-node","Using the template node",[15,22739,22740,22741,22745,22746,22751],{},"Like the ",[63,22742,22744],{"href":22743},"\u002Fnode-red\u002Fcore-nodes\u002Ftemplate","template core node",", the dashboard package\ncomes with ",[63,22747,22750],{"href":22748,"rel":22749},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-template.html",[67],"a template node of its own",".\nIf we take the HTML from the Vuetify docs pages and copy it in a template node\nthe spinner will show up on the dashboard.",[15,22753,22754],{},[165,22755],{"alt":22756,"src":22757,"title":22758},"\"Custom widget on Dashboard 2.0\"","\u002Fblog\u002F2023\u002F10\u002Fimages\u002Fcustom-element-dashboard.png","Custom widget on Dashboard 2.0",[31,22760,22762],{"id":22761},"dynamic-templates","Dynamic templates",[15,22764,22765,22766,22771],{},"While a custom element on a page is cool, and shows you can inject arbitrary HTML\non a Dashboard, it's even better if we could make the element dynamic. So let's\nstart with a first dynamic element. The quickest way to get that done is have\nan ",[63,22767,22769],{"href":22768},"\u002Fnode-red\u002Fcore-nodes\u002Finject",[924,22770,1215],{}," node output a random number every second.",[15,22773,22774,22775,22777,22778,22781],{},"So let's hook up an Inject, with ",[924,22776,1227],{},"'s output being a JSONata expression\n",[924,22779,22780],{},"$round($random() * 100)"," to generate a random number. And let's make sure it\nsends a message every second.",[15,22783,22784],{},"Then we need to update the template node to the following snippet:",[1756,22786,22788],{"className":17125,"code":22787,"language":8779,"meta":103,"style":103},"\u003Cv-progress-circular v-model=\"msg.payload\" :size=\"128\" :width=\"12\">\u003C\u002Fv-progress-circular>\n",[924,22789,22790],{"__ignoreMap":103},[1107,22791,22792,22794,22796,22798,22800,22802,22804,22806,22808,22810,22812,22814,22816,22818,22820,22822,22824,22826,22828,22830],{"class":1765,"line":1766},[1107,22793,17136],{"class":1783},[1107,22795,21505],{"class":1946},[1107,22797,17158],{"class":1775},[1107,22799,1784],{"class":1783},[1107,22801,1970],{"class":1783},[1107,22803,1227],{"class":1966},[1107,22805,1970],{"class":1783},[1107,22807,21520],{"class":1775},[1107,22809,1784],{"class":1783},[1107,22811,1970],{"class":1783},[1107,22813,22714],{"class":1966},[1107,22815,1970],{"class":1783},[1107,22817,21543],{"class":1775},[1107,22819,1784],{"class":1783},[1107,22821,1970],{"class":1783},[1107,22823,22725],{"class":1966},[1107,22825,1970],{"class":1783},[1107,22827,17215],{"class":1783},[1107,22829,21505],{"class":1946},[1107,22831,17142],{"class":1783},[15,22833,22834,22835,22838,22839,22841],{},"The difference is subtle, but important. Instead of hard-coding the ",[924,22836,22837],{},"model-value","\nto 20, the tag has changed name and it's set to ",[924,22840,1227],{},". The latter makes\nthe value dynamic.",[15,22843,22844,22845,1242,22847,22850,22851,22856],{},"Changing ",[924,22846,22837],{},[924,22848,22849],{},"v-model"," is due to leaking implementation details of\nDashboard 2.0. It uses VueJS to provide, among other features, easy updating of\ncomponents. If components are dynamic, ",[170,22852,22853,22854],{},"always use ",[924,22855,22849],{},". This allows VueJS\nto pick up changes made dynamically.",[15,22858,22859],{},[165,22860],{"alt":22861,"src":22862,"title":22863},"\"Progress spinner, random values\"","\u002Fblog\u002F2023\u002F10\u002Fimages\u002Frandom-progress-element.gif","Progress spinner, random values",[399,22865,22867],{"id":22866},"finishing-the-count-down-timer","Finishing the count down timer",[15,22869,22870],{},"This is mostly a programmers job, but it's not hard, so let's get to it. A button\nwould be great to reset the timer, and for the sake of this post we can hardcode\nthe deadline to 1m from the button press.",[15,22872,22873,22874,22877,22878,22881,22882,22884],{},"When dragging in a button node, connect it to a ",[63,22875,9518],{"href":22876},"\u002Fnode-red\u002Fcore-nodes\u002Fchange","\nnode. In the change node set the flow variable ",[924,22879,22880],{},"flow.deadline"," to the timestamp. The\nInject node from earlier needs updating to inject the ",[924,22883,22880],{},". All that's\nleft is calculating how many seconds passed, and normalizing 60 seconds to the\nrange between 0-100.",[15,22886,22887],{},"The complete flow is:",[15,22889,4917,22890,1150],{},[1107,22891,22892,22893,22895,22896,22898,22899,22901,22902,22905,22906,22909],{},"{\"id\":\"ce9bb8f74e3fc934\",\"type\":\"ui-template\",\"z\":\"24065a0aadb305e3\",\"group\":\"8fa772a709ae3316\",\"dashboard\":\"e5a3f4cdb11e5e3b\",\"page\":\"5bedf7f49d5a6037\",\"name\":\"Progress spinner\",\"order\":0,\"width\":0,\"height\":0,\"format\":\"\u003Cv-progress-circular v-model=\"msg.payload\" ",[21800,22894],{},"=\"128\" ",[21803,22897],{},"=\"12\">\\n\",\"storeOutMessages\":true,\"fwdInMessages\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":810,\"y\":80,\"wires\":[[]]},{\"id\":\"8f3e6631414aa096\",\"type\":\"inject\",\"z\":\"24065a0aadb305e3\",\"name\":\"Inject deadline\",\"props\":",[1107,22900,5884],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"deadline\",\"payloadType\":\"flow\",\"x\":140,\"y\":80,\"wires\":[[\"293cd6f9d727fa02\"]]},{\"id\":\"bd9032719d24a53d\",\"type\":\"ui-button\",\"z\":\"24065a0aadb305e3\",\"group\":\"8fa772a709ae3316\",\"name\":\"\",\"label\":\"Reset\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"topic\":\"deadline\",\"topicType\":\"msg\",\"x\":170,\"y\":140,\"wires\":[[\"61ef83d8b06ff626\"]]},{\"id\":\"61ef83d8b06ff626\",\"type\":\"change\",\"z\":\"24065a0aadb305e3\",\"name\":\"\",\"rules\":",[1107,22903,22904],{},"{\"t\":\"set\",\"p\":\"deadline\",\"pt\":\"flow\",\"to\":\"\",\"tot\":\"date\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":350,\"y\":140,\"wires\":[[]]},{\"id\":\"293cd6f9d727fa02\",\"type\":\"change\",\"z\":\"24065a0aadb305e3\",\"name\":\"Secs since reset\",\"rules\":",[1107,22907,22908],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"($millis() - msg.payload)\u002F1000\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":340,\"y\":80,\"wires\":[[\"9742da7e74fd3cd2\"]]},{\"id\":\"9742da7e74fd3cd2\",\"type\":\"range\",\"z\":\"24065a0aadb305e3\",\"minin\":\"0\",\"maxin\":\"60\",\"minout\":\"0\",\"maxout\":\"100\",\"action\":\"clamp\",\"round\":false,\"property\":\"payload\",\"name\":\"Seconds to percentages\",\"x\":570,\"y\":80,\"wires\":[[\"ce9bb8f74e3fc934\"]]},{\"id\":\"8fa772a709ae3316\",\"type\":\"ui-group\",\"name\":\"Group Name\",\"page\":\"5bedf7f49d5a6037\",\"width\":\"6\",\"height\":\"1\",\"order\":\"\",\"disp\":true},{\"id\":\"e5a3f4cdb11e5e3b\",\"type\":\"ui-base\",\"name\":\"UI Name\",\"path\":\"\u002Fdashboard\"},{\"id\":\"5bedf7f49d5a6037\",\"type\":\"ui-page\",\"name\":\"Page Name\",\"ui\":\"e5a3f4cdb11e5e3b\",\"path\":\"\u002F\",\"layout\":\"grid\",\"theme\":\"8240fbe7c09bc81c\"},{\"id\":\"8240fbe7c09bc81c\",\"type\":\"ui-theme\",\"name\":\"Theme Name\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"}}",[2582,22911,21846],{},{"title":103,"searchDepth":104,"depth":104,"links":22913},[22914,22917],{"id":22666,"depth":107,"text":22667,"children":22915},[22916],{"id":22736,"depth":294,"text":22737},{"id":22761,"depth":107,"text":22762,"children":22918},[22919],{"id":22866,"depth":294,"text":22867},"2023-10-02","Expand your dashboard with the full collection of Vuetify components","blog\u002F2023\u002F10\u002Fimages\u002Ftile-blog-custom-vuetify-components-for-Dashboard.png",{"excerpt":22924},{"type":12,"value":22925},[22926],[15,22927,22651,22928,22655],{},[170,22929,22654],{},"\u002Fblog\u002F2023\u002F10\u002Fcustom-vuetify-components-dashboard",{"title":21789,"description":22921},{"loc":22930},"blog\u002F2023\u002F10\u002Fcustom-vuetify-components-dashboard",[8650,7457,138,12631],"KO_LrX3IbVRXKJstHOLgJyEUJhvBN9uquLYkw9FdhGM",{"id":22937,"title":22938,"authors":22939,"body":22940,"cta":3,"date":23049,"description":23050,"extension":116,"image":23051,"lastUpdated":3,"meta":23052,"navigation":129,"path":23057,"seo":23058,"sitemap":23059,"stem":23060,"subtitle":23061,"tags":23062,"tldr":3,"video":3,"__hash__":23063},"blog\u002Fblog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers.md","How ChatGPT improves Node-RED Developer Experience",[2622],{"type":12,"value":22941,"toc":23041},[22942,22945,22949,22953,22967,22973,22987,22991,22994,23000,23008,23016,23020,23034,23038],[15,22943,22944],{},"ChatGPT has the potential to have a significant impact on the Node-RED community. It is a powerful language model that can be used to generate flows, interpret them, and provide documentation, maybe soon even write the flow! The combination of ChatGPT, or generative AI at large, with Node-RED can significantly improve the developer experience with Node-RED. In this post we’ll review what the community has already built.",[31,22946,22948],{"id":22947},"how-generative-ai-like-chatgpt-is-used-for-node-red","How generative AI like ChatGPT is used for Node-RED",[399,22950,22952],{"id":22951},"function-node-3-chatgpt","Function node \u003C3 ChatGPT",[15,22954,22955,22956,2642,22961,22966],{},"ChatGPT, and other models, can write code for you, much like ",[63,22957,22960],{"href":22958,"rel":22959},"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Fcopilot",[67],"GitHub CoPilot",[63,22962,22965],{"href":22963,"rel":22964},"https:\u002F\u002Fabout.gitlab.com\u002Fgitlab-duo\u002F",[67],"GitLab Duo",". As Node-RED is ‘low-code’ the ability for generative AI to write the required code for you creates a paradigm shift to ‘no-code’!",[15,22968,22969],{},[165,22970],{"alt":22971,"src":22972},"Example of Chat GPT to generate contents of a function node","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fchatgpt-fcn-example.png",[15,22974,22975,22976,22980,22981,22986],{},"At FlowFuse we’ve written about this ",[63,22977,22979],{"href":22978},"\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node\u002F","before",", and published a ",[63,22982,22985],{"href":22983,"rel":22984},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt",[67],"plugin",". This node allows flow developers to be more productive and efficient. While this works only for the function node, there’s countless other possibilities to describe a flow in text and import a ChatGPT generated flow that are on the horizon!",[399,22988,22990],{"id":22989},"flow-interpretation","Flow Interpretation",[15,22992,22993],{},"When developing larger projects with multiple tabs, it’s important to understand what each tab contributes to the full project. This problem is compounded when the flows are developed by a team or the time between the flow was last updated is higher.",[15,22995,22996],{},[165,22997],{"alt":22998,"src":22999},"ChatGPT Flow Interpretation","https:\u002F\u002Fraw.githubusercontent.com\u002Fnode-red-jp\u002Fnode-red-contrib-plugin-chatgpt\u002Fmain\u002Finfotab.png",[15,23001,23002,23007],{},[63,23003,23006],{"href":23004,"rel":23005},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fkazuhitoyokoi\u002F",[67],"Kazuhito-san"," wrote a module for Node-RED to interpret the flow, nodes, and their order into a well structured documentation. Through a click of a button it's generated by the well-known OpenAI\nmodel. This is especially interesting as it's thus able regenerate it when changes were made by the developers.",[15,23009,23010,23011,29],{},"It’s a plugin that requires very little setup, and can be found in the ",[63,23012,23015],{"href":23013,"rel":23014},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fnode-red-contrib-plugin-chatgpt",[67],"flow library",[399,23017,23019],{"id":23018},"lots-of-plugins","Lots of plugins",[15,23021,23022,23023,9079,23028,23033],{},"The ecosystem of Node-RED has always been a fast adopter of new technology. There's\nnodes for ",[63,23024,23027],{"href":23025,"rel":23026},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-chatgpt",[67],"ChatGPT",[63,23029,23032],{"href":23030,"rel":23031},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-bard",[67],"Google's Bard",", and many\nmore. These plugins genernally let you build on top of these models, and don't\nnessecairly improve the developer experience. It's however a great source of\ninspiration!",[399,23035,23037],{"id":23036},"further-discussion","Further discussion",[15,23039,23040],{},"These were three examples of how generative AI is used in the Node-RED community. Please let us know if you're using ChatGPT or other AI models with Node-RED? And what would be the killer feature for Node-RED and AI?",{"title":103,"searchDepth":104,"depth":104,"links":23042},[23043],{"id":22947,"depth":107,"text":22948,"children":23044},[23045,23046,23047,23048],{"id":22951,"depth":294,"text":22952},{"id":22989,"depth":294,"text":22990},{"id":23018,"depth":294,"text":23019},{"id":23036,"depth":294,"text":23037},"2023-09-23","Discover how ChatGPT enhances Node-RED development, from generating code to interpreting flows, and explore its impact on the community.","blog\u002F2023\u002F09\u002Fimages\u002Fchatgpt-node-red-dx-tile.png",{"excerpt":23053},{"type":12,"value":23054},[23055],[15,23056,22944],{},"\u002Fblog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers",{"title":22938,"description":23050},{"loc":23057},"blog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers","Language models have made an impact in the Node-RED community",[8650,138,22403,2784],"d3UgvEBS6sf-bT_vjtJKTXCibuM_ibqixmPLqSGGNII",{"id":23065,"title":23066,"authors":23067,"body":23068,"cta":3,"date":23346,"description":23347,"extension":116,"image":23348,"lastUpdated":3,"meta":23349,"navigation":129,"path":23359,"seo":23360,"sitemap":23361,"stem":23362,"subtitle":23363,"tags":23364,"tldr":3,"video":3,"__hash__":23365},"blog\u002Fblog\u002F2023\u002F09\u002Fflow-viewer.md","Share & Preview Flows on flows.nodered.org",[8417],{"type":12,"value":23069,"toc":23339},[23070,23078,23081,23085,23100,23107,23111,23119,23127,23131,23134,23138,23142,23157,23231,23240,23261,23334,23337],[15,23071,23072,23073],{},"For years, Node-RED's website has provided functionality to share flows through ",[63,23074,23077],{"href":23075,"rel":23076},"https:\u002F\u002Fflows.nodered.org",[67],"flows.nodered.org",[15,23079,23080],{},"This week, we at FlowFuse have contributed a new feature to the site that allows users to visually preview those flows, and embed\u002Fshare those flows in articles and on forum posts.",[31,23082,23084],{"id":23083},"visual-flow-previews","Visual Flow Previews",[15,23086,23087,23088,23093,23094,23099],{},"A huge thank you for this work needs to go Gerrit Riessen's work published on his ",[63,23089,23092],{"href":23090,"rel":23091},"https:\u002F\u002Fblog.openmindmap.org\u002F",[67],"Open Mind Map Blog",". He recently open-sourced some great work to GitHub (",[63,23095,23098],{"href":23096,"rel":23097},"https:\u002F\u002Fgithub.com\u002Fgorenje\u002Fnode-red-flowviewer-js",[67],"repo","), and with some adaptation and collaboration, we've been able to utilise this as a foundation for the functionality we've added into the flows site.",[15,23101,23102,23103,23106],{},"Adding this to ",[63,23104,23077],{"href":23075,"rel":23105},[67]," will make it far easier to learn how others use Node-RED, and to share your own flows with others too. The embedding functionality should also make talking about Node-RED in your own articles & forums much easier.",[399,23108,23110],{"id":23109},"example-simple-flow","Example: Simple Flow",[15,23112,23113,23114,23116,23117,1754],{},"Here's a demonstration of a simple ",[924,23115,1215],{}," > ",[924,23118,1324],{},[23120,23121],"iframe",{"width":23122,"height":23123,"src":23124,"allow":23125,"style":23126},"100%","200px","https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F500ee13719e54e42493c8ec96fa733b6\u002Fshare?height=100","clipboard-read; clipboard-write","border: none;",[399,23128,23130],{"id":23129},"example-subflows-groups-links-switches","Example: Subflows, Groups, Links & Switches",[15,23132,23133],{},"Here's a non-functional flow that just demonstrates how FlowViewer renders the range of node types available in Node-RED:",[23120,23135],{"width":23122,"height":23136,"src":23137,"allow":23125,"style":23126},"500px","https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F82a8602b615740491d30c083e5292e5f\u002Fshare",[31,23139,23141],{"id":23140},"sharing-embedding-flows","Sharing & Embedding Flows",[15,23143,23144,23145,23148,23149,23152,23153,23156],{},"Any flow on ",[63,23146,23077],{"href":23075,"rel":23147},[67]," now has a ",[924,23150,23151],{},"Share Flow"," option in the ",[924,23154,23155],{},"Actions"," section on the right side of the flows page. Clicking this will provide you with an iframe like:",[1756,23158,23160],{"className":17125,"code":23159,"language":8779,"meta":103,"style":103},"\u003Ciframe width=\"100%\" height=\"100%\"\n    src=\"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F7c2dd3ccde70746a40ef8f5aa58c591c\u002Fshare\"\n    allow=\"clipboard-read; clipboard-write\" style=\"border: none;\">\u003C\u002Fiframe>\n",[924,23161,23162,23188,23202],{"__ignoreMap":103},[1107,23163,23164,23166,23168,23170,23172,23174,23176,23178,23180,23182,23184,23186],{"class":1765,"line":1766},[1107,23165,17136],{"class":1783},[1107,23167,23120],{"class":1946},[1107,23169,17489],{"class":1775},[1107,23171,1784],{"class":1783},[1107,23173,1970],{"class":1783},[1107,23175,23122],{"class":1966},[1107,23177,1970],{"class":1783},[1107,23179,21619],{"class":1775},[1107,23181,1784],{"class":1783},[1107,23183,1970],{"class":1783},[1107,23185,23122],{"class":1966},[1107,23187,17207],{"class":1783},[1107,23189,23190,23193,23195,23197,23200],{"class":1765,"line":107},[1107,23191,23192],{"class":1775},"    src",[1107,23194,1784],{"class":1783},[1107,23196,1970],{"class":1783},[1107,23198,23199],{"class":1966},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F7c2dd3ccde70746a40ef8f5aa58c591c\u002Fshare",[1107,23201,17207],{"class":1783},[1107,23203,23204,23207,23209,23211,23213,23215,23217,23219,23221,23223,23225,23227,23229],{"class":1765,"line":294},[1107,23205,23206],{"class":1775},"    allow",[1107,23208,1784],{"class":1783},[1107,23210,1970],{"class":1783},[1107,23212,23125],{"class":1966},[1107,23214,1970],{"class":1783},[1107,23216,19840],{"class":1775},[1107,23218,1784],{"class":1783},[1107,23220,1970],{"class":1783},[1107,23222,23126],{"class":1966},[1107,23224,1970],{"class":1783},[1107,23226,17215],{"class":1783},[1107,23228,23120],{"class":1946},[1107,23230,17142],{"class":1783},[15,23232,23233,23234,23239],{},"Which you can paste\u002Fembed into any website or blog post. Nick has also ",[63,23235,23238],{"href":23236,"rel":23237},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fpreviewing-flows-on-the-flow-library\u002F",[67],"enabled the Node-RED forums to support these embeds too",", and is also how we've embeded the above flows too.",[15,23241,23242,23243,23246,23247,23250,23251,23253,23254,23257,23258,23260],{},"If you want more control over the sizing of the viewer, you can also include a ",[924,23244,23245],{},"?height="," query parameter on the ",[924,23248,23249],{},"src"," value of the ",[924,23252,23120],{},". You may also need to hardcode the ",[924,23255,23256],{},"height"," property of the ",[924,23259,23120],{}," itself to account for this change, depending on where you're embedding it to. For example:",[1756,23262,23264],{"className":17125,"code":23263,"language":8779,"meta":103,"style":103},"\u003Ciframe width=\"100%\" height=\"250px\"\n    src=\"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F7c2dd3ccde70746a40ef8f5aa58c591c\u002Fshare?height=100\"\n    allow=\"clipboard-read; clipboard-write\" style=\"border: none;\">\u003C\u002Fiframe>\n",[924,23265,23266,23293,23306],{"__ignoreMap":103},[1107,23267,23268,23270,23272,23274,23276,23278,23280,23282,23284,23286,23288,23291],{"class":1765,"line":1766},[1107,23269,17136],{"class":1783},[1107,23271,23120],{"class":1946},[1107,23273,17489],{"class":1775},[1107,23275,1784],{"class":1783},[1107,23277,1970],{"class":1783},[1107,23279,23122],{"class":1966},[1107,23281,1970],{"class":1783},[1107,23283,21619],{"class":1775},[1107,23285,1784],{"class":1783},[1107,23287,1970],{"class":1783},[1107,23289,23290],{"class":1966},"250px",[1107,23292,17207],{"class":1783},[1107,23294,23295,23297,23299,23301,23304],{"class":1765,"line":107},[1107,23296,23192],{"class":1775},[1107,23298,1784],{"class":1783},[1107,23300,1970],{"class":1783},[1107,23302,23303],{"class":1966},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F7c2dd3ccde70746a40ef8f5aa58c591c\u002Fshare?height=100",[1107,23305,17207],{"class":1783},[1107,23307,23308,23310,23312,23314,23316,23318,23320,23322,23324,23326,23328,23330,23332],{"class":1765,"line":294},[1107,23309,23206],{"class":1775},[1107,23311,1784],{"class":1783},[1107,23313,1970],{"class":1783},[1107,23315,23125],{"class":1966},[1107,23317,1970],{"class":1783},[1107,23319,19840],{"class":1775},[1107,23321,1784],{"class":1783},[1107,23323,1970],{"class":1783},[1107,23325,23126],{"class":1966},[1107,23327,1970],{"class":1783},[1107,23329,17215],{"class":1783},[1107,23331,23120],{"class":1946},[1107,23333,17142],{"class":1783},[15,23335,23336],{},"We know it's still not perfect, and there's plenty more we can do with it, but hopefully this is a welcome contribution to the Node-RED community.",[2582,23338,21846],{},{"title":103,"searchDepth":104,"depth":104,"links":23340},[23341,23345],{"id":23083,"depth":107,"text":23084,"children":23342},[23343,23344],{"id":23109,"depth":294,"text":23110},{"id":23129,"depth":294,"text":23130},{"id":23140,"depth":107,"text":23141},"2023-09-20","Explore FlowFuse's latest contribution to flows.nodered.org, an interactive \"Flow Viewer\" for sharing and embedding flows in articles and forum posts.","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Ftile-flowviewer.jpg",{"excerpt":23350},{"type":12,"value":23351},[23352,23357],[15,23353,23072,23354],{},[63,23355,23077],{"href":23075,"rel":23356},[67],[15,23358,23080],{},"\u002Fblog\u002F2023\u002F09\u002Fflow-viewer",{"title":23066,"description":23347},{"loc":23359},"blog\u002F2023\u002F09\u002Fflow-viewer","FlowFuse has just contributed an interactive \"Flow Viewer\" to flows.nodered.org, allowing users to preview flows, and embed them in articles & forum posts.",[8650,136,137,10,138],"0w-F6e3TNFPhy8e5PcIorhjNA_TlI9Nwz4C3UElZLRM",{"id":23367,"title":23368,"authors":23369,"body":23370,"cta":3,"date":23743,"description":23744,"extension":116,"image":23745,"lastUpdated":6064,"meta":23746,"navigation":129,"path":23757,"seo":23758,"sitemap":23759,"stem":23760,"subtitle":23761,"tags":23762,"tldr":3,"video":3,"__hash__":23763},"blog\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data-part2.md","Modernize your legacy industrial data. Part 2.",[6225],{"type":12,"value":23371,"toc":23730},[23372,23383,23386,23390,23397,23419,23422,23426,23429,23432,23516,23519,23523,23526,23529,23538,23541,23544,23553,23556,23559,23563,23566,23569,23592,23596,23599,23602,23606,23609,23612,23620,23623,23632,23635,23639,23642,23651,23654,23657,23661,23667,23670,23679,23682,23685,23694,23698,23701,23714,23719,23721,23724,23727],[15,23373,23374,23375,23379,23380,23382],{},"In ",[63,23376,23378],{"href":23377},"\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data\u002F","part 1 of this series",", I introduced the topic of working with legacy industrial data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world. We looked at some of the challenges and how Node-RED with ",[924,23381,7287],{}," node can help.",[15,23384,23385],{},"In this article, I will dive a little deeper into the topic and discuss some of the finer details. I hope to demonstrate a smarter approach that can make a huge difference to data accuracy, performance and maintainability while significantly reducing developer time. Not only that, ending with a no-code solution.",[31,23387,23389],{"id":23388},"obtaining-industrial-data","Obtaining Industrial Data",[15,23391,23392,23393,23396],{},"In order to convert the legacy data to a format more suited to IIoT we first need to ",[170,23394,23395],{},"grab"," that data.",[15,23398,23399,23400,853,23403,853,23406,853,23409,853,23412,1413,23415,23418],{},"Node-RED has core nodes that can help you and many more contribution nodes exist that provide access to a wide range of industrial devices. To give you an idea, ",[924,23401,23402],{},"node-red-contrib-modbus",[924,23404,23405],{},"node-red-contrib-s7comm",[924,23407,23408],{},"node-red-contrib-omron-fins",[924,23410,23411],{},"node-red-contrib-mcprotocol",[924,23413,23414],{},"node-red-contrib-df1",[924,23416,23417],{},"node-red-contrib-cip-st-ethernet-ip"," are just some of the PLC data access nodes available.",[15,23420,23421],{},"But getting the data is just the beginning, it's the methods and considerations you need to make that can make the difference between success and failure. Read on...",[31,23423,23425],{"id":23424},"data-consistency","Data consistency",[15,23427,23428],{},"An often overlooked aspect of working with legacy industrial data is the consistency of the data being read. In the context of this article, consistency means that the multiple values that make up a related data set are read in a way that they are all valid to one another at the point in time it was read.",[15,23430,23431],{},"Let's take a look at a simple example. We have a process PLC recording production metrics and wish to get this data from its Modbus interface for reporting and decision making. The PLC has 5 values that we need to read:",[6376,23433,23434,23448],{},[6379,23435,23436],{},[6382,23437,23438,23441,23444,23446],{},[6385,23439,23440],{},"Register",[6385,23442,23443],{},"Value",[6385,23445,6404],{},[6385,23447,6399],{},[6406,23449,23450,23464,23478,23491,23503],{},[6382,23451,23452,23455,23458,23461],{},[6411,23453,23454],{},"1, 2",[6411,23456,23457],{},"10",[6411,23459,23460],{},"Part Count",[6411,23462,23463],{},"UINT32",[6382,23465,23466,23469,23472,23475],{},[6411,23467,23468],{},"3, 4",[6411,23470,23471],{},"2.5",[6411,23473,23474],{},"Cycle Time (sec)",[6411,23476,23477],{},"UINT32\u002F100",[6382,23479,23480,23483,23486,23489],{},[6411,23481,23482],{},"5, 6",[6411,23484,23485],{},"30",[6411,23487,23488],{},"Production Time (sec)",[6411,23490,23477],{},[6382,23492,23493,23496,23498,23501],{},[6411,23494,23495],{},"7, 8",[6411,23497,21538],{},[6411,23499,23500],{},"Run Time (sec)",[6411,23502,23477],{},[6382,23504,23505,23508,23511,23514],{},[6411,23506,23507],{},"9, 10",[6411,23509,23510],{},"11",[6411,23512,23513],{},"Stoppage Time (sec)",[6411,23515,23477],{},[15,23517,23518],{},"In the above data sample, we can see the total production time is 30 seconds and the run time is 20 seconds. The expectation is that the Stoppage Time should be 10 seconds. However, as we can see, Stoppage Time in this sample is 11 seconds. That is because this data is not consistent.",[399,23520,23522],{"id":23521},"why-is-the-data-inconsistent","Why is the data inconsistent?",[15,23524,23525],{},"The most common reason for the data inconsistency is that the data is being read from the PLC while the PLC is running. The data is changing as it is being read.",[15,23527,23528],{},"Typically this happens when a developer, unfamiliar with the protocol or end device, begins by reading the data individually. Here is how this journey might look:",[15,23530,23531,23534],{},[170,23532,23533],{},"Image 1: individual reads",[165,23535],{"alt":23536,"src":23537},"image showing 10 individual reads","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-demo1.png",[15,23539,23540],{},"That is a lot of nodes and a lot of duplication!",[15,23542,23543],{},"What is worse, is that the developer continues down this path and begins converting the data ready for publishing to MQTT. Here is how this might evolve:",[15,23545,23546,23549],{},[170,23547,23548],{},"Image 2: individual reads with data processing",[165,23550],{"alt":23551,"src":23552},"Image showing 10 individual reads with data processing","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-10polls.gif",[15,23554,23555],{},"Yippie! We have the data, it works, we publish it to MQTT, job done. Right?",[15,23557,23558],{},"Unfortunately, no. The data is inconsistent.",[399,23560,23562],{"id":23561},"so-whats-the-big-deal","So whats the big deal?",[15,23564,23565],{},"Inconsistent data is not useful data and errors can get compounded over time. This leads to bad decisions being made and a loss of confidence in the data. Ultimately, this leads to the data being ignored and the opportunities to make improvements are lost. Loss of improvements means loss of money!",[15,23567,23568],{},"Not only that, from a developer or maintainers perspective, it has many problems:",[271,23570,23571,23574,23577,23580,23583,23586,23589],{},[42,23572,23573],{},"Lots of error prone manual configuration (yes, I made several mistakes while creating the example)",[42,23575,23576],{},"Hard coded register addresses",[42,23578,23579],{},"Duplication",[42,23581,23582],{},"Inextensible - what if we need to read more registers?",[42,23584,23585],{},"Inconsistent data - as discussed above",[42,23587,23588],{},"Slow - each read takes time",[42,23590,23591],{},"Inefficient use of network bandwidth - each read requires a request and response packet",[399,23593,23595],{"id":23594},"how-can-we-make-the-data-consistent","How can we make the data consistent?",[15,23597,23598],{},"The most obvious solution is to stop the PLC before reading the data.\nHowever, I am faily certain your boss will not be super pleased with stopping the manufacturing process.\nThe next best thing is for the PLC to sample and store the data in an internal memory buffer, waiting, unchanging, to be collected. Unfortunately, this too is not always possible either due to limited in-house skills, locked down PLCs or simply because the PLC does not have the memory to store the data.",[15,23600,23601],{},"The next best thing to do is to read relative data as quickly as possible and in one block.",[399,23603,23605],{"id":23604},"a-quick-side-bar-timing-is-everything","A quick side-bar (timing is ... everything)",[15,23607,23608],{},"In many protocols, including Modbus, data must be polled. Each poll, depending on many factors, can take a number of milliseconds.",[15,23610,23611],{},"Lets say, on a relatively quiet serial network, a single register poll takes 25 milliseconds (assume 9600 baud, 1 byte\u002Fms, request packet size 8 bytes, response packet size 7 bytes, 10ms latency for the PLC to receive, process and respond to the request)",[39,23613,23614,23617],{},[42,23615,23616],{},"If we read 10 registers individually, then the time taken to read the 10 registers is 250 milliseconds.",[42,23618,23619],{},"If we actually needed a more realistic number of registers, say 32, then the time taken to read them individually is a whopping 800 milliseconds. In the world of PLCs, that is an eternity.",[15,23621,23622],{},"Now, lets look at that from a different approach. If we were to read the 32 registers in one go, the time taken based on the above constants would be 87 milliseconds. That is over 9 times faster than reading the registers individually. The improvements don't stop there either, the number of total bytes transferred on your network is reduced by 84% and, more importantly, the data is consistent since it was read from the same scan of the PLC scan.",[15,23624,23625,23628],{},[170,23626,23627],{},"Image 3: A comparison of individual reads vs block reads",[165,23629],{"alt":23630,"src":23631},"A data table comparing polls vs block read","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-table.png",[15,23633,23634],{},"OK, back to the topic at hand.",[31,23636,23638],{"id":23637},"getting-the-data-in-a-more-consistent-way","Getting the data in a more consistent way",[15,23640,23641],{},"Here is the same example as above but this time we are reading the data in one go:",[15,23643,23644,23647],{},[170,23645,23646],{},"Image 4: Getting data in one block",[165,23648],{"alt":23649,"src":23650},"image showing 1 read for 10 registers","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-demo1b.png",[15,23652,23653],{},"Note how much simpler this is? Not only that, it is easier to maintain, faster, more extensible and most importantly, the data is consistent.",[15,23655,23656],{},"Great, lets move on.",[31,23658,23660],{"id":23659},"processing-the-data-in-readiness-for-iiot-mqtt","Processing the data in readiness for IIoT: MQTT",[15,23662,23663,23664,23666],{},"Now that we have good data, we need to process it in readiness for IIoT. In this example, we are going to publish the data to an MQTT broker as individual topics. This is a common approach as it allows the data to be easily consumed by other systems and applications. Using ",[924,23665,7287],{}," we can easily convert the data into more meaningful formats.",[15,23668,23669],{},"The first, instinctive approach is to fan out the data and process it individually:",[15,23671,23672,23675],{},[170,23673,23674],{},"Image 5: block reads, individual processing",[165,23676],{"alt":23677,"src":23678},"image showing 1 modbus poll with individual processing","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-1poll-fixed.gif",[15,23680,23681],{},"This may be fine for a handful of registers but it soon becomes unwieldy and unmaintainable.",[15,23683,23684],{},"But lets be smarter about this. We know that the data is consistent and we know that we can read it in one go. So, lets process it in one go too:",[15,23686,23687,23690],{},[170,23688,23689],{},"Image 6: block reads, smart processing, no-code solution",[165,23691],{"alt":23692,"src":23693},"image showing 1 modbus poll with smart processing","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-pt2-1poll-extensible.gif",[31,23695,23697],{"id":23696},"node-red-in-production","Node-RED in Production",[15,23699,23700],{},"Node-RED is a powerful tool widely used in IoT and IIoT industries, including manufacturing, automotive, textiles, and building management. It excels at collecting, transforming, visualizing, and analyzing data. While integrating Node-RED into production environments offers numerous benefits, it often involves complex tasks such as deploying the server, managing security, and ensuring scalability. These initial setup challenges can be overwhelming and time-consuming.",[15,23702,23703,23704,23708,23709,23713],{},"FlowFuse simplifies these tasks by providing a unified platform for managing all Node-RED instances. It enhances collaboration, ensures security, and supports scalability, making deployment and management more efficient. With features like ",[63,23705,23707],{"href":23706},"\u002Fdocs\u002Fuser\u002Fsnapshots\u002F","snapshots",", team collaboration tools, one-click deployment, and ",[63,23710,23712],{"href":23711},"\u002Fdocs\u002Fuser\u002Fuser-settings\u002F#security","multi-factor authentication",", FlowFuse streamlines the process and enhances the operational capabilities of Node-RED in production settings.",[15,23715,23716],{},[19,23717,23718],{},"[Sign up]({% include \"sign-up-url.njk\" %}) now for a free trial and experience FlowFuse's features",[31,23720,19466],{"id":19465},[15,23722,23723],{},"I hope this article has given you some food for thought and some ideas on not only simplifying your journey to IIoT but also the pitfalls to avoid along the way.",[15,23725,23726],{},"A parting thought, there are times when the data is not contiguous. There are ways to deal with this too but that is for another day.",[15,23728,23729],{},"P.S. I will post the flows used for the examples above in the comments below. If you have any questions or comments, please reach out there too.",{"title":103,"searchDepth":104,"depth":104,"links":23731},[23732,23733,23739,23740,23741,23742],{"id":23388,"depth":107,"text":23389},{"id":23424,"depth":107,"text":23425,"children":23734},[23735,23736,23737,23738],{"id":23521,"depth":294,"text":23522},{"id":23561,"depth":294,"text":23562},{"id":23594,"depth":294,"text":23595},{"id":23604,"depth":294,"text":23605},{"id":23637,"depth":107,"text":23638},{"id":23659,"depth":107,"text":23660},{"id":23696,"depth":107,"text":23697},{"id":19465,"depth":107,"text":19466},"2023-09-17","A deeper dive into making the most of legacy industrial data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world.","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-blog-image.png",{"excerpt":23747},{"type":12,"value":23748},[23749,23755],[15,23750,23374,23751,23379,23753,23382],{},[63,23752,23378],{"href":23377},[924,23754,7287],{},[15,23756,23385],{},"\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data-part2",{"title":23368,"description":23744},{"loc":23757},"blog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data-part2","Explore strategies, challenges, and smart processing techniques for enhancing legacy industrial data utilization in the IIoT era with Node-RED.",[8650,138,137],"cHr6uWbcZ7FKkFwNU1CoAlU3DZjm00br7A0zyFSAcXc",{"id":23765,"title":23766,"authors":23767,"body":23768,"cta":3,"date":24005,"description":24006,"extension":116,"image":23745,"lastUpdated":6064,"meta":24007,"navigation":129,"path":24014,"seo":24015,"sitemap":24016,"stem":24017,"subtitle":24018,"tags":24019,"tldr":3,"video":3,"__hash__":24020},"blog\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data.md","Modernize your legacy industrial data",[6225],{"type":12,"value":23769,"toc":23987},[23770,23773,23776,23780,23783,23787,23790,23794,23805,23809,23818,23822,23832,23836,23846,23850,23856,23862,23865,23885,23889,23892,23895,23899,23902,23906,23913,23917,23928,23932,23939,23943,23946,23954,23958,23962,23965],[15,23771,23772],{},"Industrial systems generate valuable data, but legacy protocols like Modbus or non-IIoT standards often make it hard to use. Bridging this gap is essential to connect traditional systems with the modern Industrial Internet of Things (IIoT).",[15,23774,23775],{},"Whether it’s Modbus registers, serial communication without a protocol, or standards like Siemens S7 and Mitsubishi MC-Protocol, the challenge lies in making sense of this raw information. With tools like Node-RED and the node-red-contrib-buffer-parser, you can turn complex, outdated data streams into usable formats that power IIoT innovation.",[399,23777,23779],{"id":23778},"legacy-industrial-data-modbus","Legacy Industrial Data: Modbus",[15,23781,23782],{},"Modbus is one of the most widely used industrial protocols. Originally developed in the late 1970s, it has been a popular choice for industrial communication ever since. However, its data format can be challenging to work with in the context of modern IIoT applications. Modbus typically represents data in 16-bit unsigned registers, making it necessary to convert this data into more usable formats like Signed integer, Float, Signed and Unsigned Long, String, or even individual bits.",[399,23784,23786],{"id":23785},"a-short-primer-on-data-types","A short primer on data types",[15,23788,23789],{},"Before we dive into how to make sense of Modbus data, let's take a quick look at some of the common data types we have to deal with.",[1165,23791,23793],{"id":23792},"_16-bit-unsigned","16-bit unsigned",[15,23795,23796,23797,23800,23801,23804],{},"16-bit unsigned data is an integer that can only be positive. It can represent values from 0 to 65535. For example, the number 12345 is represented as ",[924,23798,23799],{},"0x3039"," in hexadecimal or ",[924,23802,23803],{},"0011000000111001"," in binary.",[1165,23806,23808],{"id":23807},"_16-bit-signed","16-bit signed",[15,23810,23811,23812,23800,23815,23804],{},"16-bit signed data is an integer that can be positive or negative. It can represent values from -32768 to 32767. For example, the number -12345 is represented as ",[924,23813,23814],{},"0xCFC7",[924,23816,23817],{},"1100111111000111",[1165,23819,23821],{"id":23820},"_32-bit-data","32-bit data",[15,23823,23824,23825,23800,23828,23831],{},"32-bit data, like 16-bit data can mean many things. It could be signed or unsigned, or even a floating point number. For example, the number 12345 is represented as ",[924,23826,23827],{},"0x00003039",[924,23829,23830],{},"00000000000000000011000000111001"," in binary. Typically, 32-bit data is represented as two 16-bit registers. Therefore, when dealing with 32-bit data, you need to combine two 16-bit registers to get the full value.",[1165,23833,23835],{"id":23834},"endianness","Endianness",[15,23837,23838,23839,23841,23842,23845],{},"Endianness, particularly in the context of data communications, refers to the order of bytes and how they are stored or transmitted. There are two types of endianness: big-endian (BE) and little-endian (LE). In big-endian, the most significant byte is first, while in little-endian, the least significant byte is first. For example, the number 12345 is represented as ",[924,23840,23799],{}," in a big-endian word and ",[924,23843,23844],{},"0x3930"," in little-endian word.  This can often cause confusion and complicate the process of converting Modbus data into more usable formats.",[399,23847,23849],{"id":23848},"node-red-and-node-red-contrib-buffer-parser-to-the-rescue","Node-RED and node-red-contrib-buffer-parser to the rescue",[15,23851,23852,23853,23855],{},"Node-RED is an open-source flow-based development tool for visual programming. It's particularly well-suited for IIoT applications because of its versatility and extensive library of nodes. One such node, ",[924,23854,7287],{},", provides a solution to the legacy data conversion challenge.",[15,23857,23858,23859,23861],{},"This powerful Node-RED module allows you to parse a Buffer of bytes or an Array of integer data (which, by no coincidence, the popular module ",[924,23860,23402],{}," outputs), and convert it into various data types. It can output pretty much any data type, including byte-swapped data, WORD swapped data, masked\u002Fshifted\u002Fscaled data, and even individual bits.",[15,23863,23864],{},"Here's a quick overview of how it works:",[271,23866,23867,23873,23879],{},[42,23868,23869,23872],{},[19,23870,23871],{},"Data Parsing",": Start by setting up a Modbus READ node in Node-RED to retrieve data from your industrial device. Then, use the buffer parser node to parse the Modbus data.",[42,23874,23875,23878],{},[19,23876,23877],{},"Data Conversion",": With the buffer parser, you can easily convert the 16-bit unsigned data into more meaningful formats. Whether you need to translate it into Float, Long, String, or even extract specific bits, this tool makes the process straightforward.",[42,23880,23881,23884],{},[19,23882,23883],{},"Publishing to MQTT, influxDB, a dashboard, an IIoT system",": Once your data is in a usable format, Node-RED enables you to publish it to many places. MQTT (Message Queuing Telemetry Transport), a popular protocol for IIoT communication is a perfect example. This makes your data accessible to other IIoT systems and applications for further analysis and action.",[399,23886,23888],{"id":23887},"unlocking-the-potential-of-legacy-data","Unlocking the Potential of Legacy Data",[15,23890,23891],{},"By leveraging Node-RED and buffer parser, you can bridge the gap between legacy industrial protocols and the IIoT world. This means you can extract valuable insights from your existing infrastructure without the need for costly hardware upgrades or replacements.",[15,23893,23894],{},"In the era of the Industrial Internet of Things, making sense of your industrial data is no longer a daunting challenge. With the right tools approach, you can unlock the full potential of your legacy data and drive efficiency, productivity, and innovation in your industrial processes. Yey!",[399,23896,23898],{"id":23897},"_3-quick-demos-of-node-red-and-the-buffer-parser-node-in-action","3 quick demos of Node-RED and the buffer parser node in action",[15,23900,23901],{},"Here are 3 quick demonstrations that barely scratch the surface of possibilities:",[1165,23903,23905],{"id":23904},"example-1-modbus-to-mqtt","Example 1: Modbus to MQTT",[15,23907,23908,23909],{},"Converting an array of 16-bit unsigned integers to String, Float and a scaled integer and passing them to an MQTT broker in 4 nodes!\n",[165,23910],{"alt":23911,"src":23912},"Legacy data to MQTT","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-to-mqtt.gif",[1165,23914,23916],{"id":23915},"example-2-modbus-to-influxdb","Example 2: Modbus to InfluxDB",[15,23918,23919,23920,23924],{},"Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to influxDB!\n",[165,23921],{"alt":23922,"src":23923},"Legacy data to influxDB","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-to-influx.png",[165,23925],{"alt":23926,"src":23927},"Legacy data to influxDB2","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-to-influx2.png",[1165,23929,23931],{"id":23930},"example-3-modbus-data-on-a-dashboard","Example 3: Modbus data on a dashboard",[15,23933,23934,23935],{},"Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to a dashboard!\n",[165,23936],{"alt":23937,"src":23938},"Legacy data to dashboard","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Findustrial-legacy-data-to-dashboard.png",[399,23940,23942],{"id":23941},"simplify-your-node-red-operations-with-flowfuse","Simplify Your Node-RED Operations with FlowFuse",[15,23944,23945],{},"While Node-RED is a fantastic tool for data collection, transformation, and analysis, integrating it into a production environment can sometimes feel like navigating a maze. Whether you’re deploying Node-RED on a server, ensuring secure remote access for your team, or managing a sprawling network of thousands of instances, it’s easy to feel overwhelmed.",[15,23947,23948,23949,23953],{},"That’s where FlowFuse steps in to make your life easier. FlowFuse is designed to tackle these challenges head-on. It enhances Node-RED with features that simplify collaboration, strengthen security, and provide scalable deployment options. Imagine having a robust system that not only keeps your Node-RED applications running smoothly but also scales effortlessly with your needs. With FlowFuse, you gain access to a comprehensive suite of production-ready ",[63,23950,23952],{"href":23951},"\u002Fplatform\u002Ffeatures\u002F","features"," designed to streamline your Node-RED workflows and boost overall performance.",[15,23955,23956],{},[19,23957,23718],{},[399,23959,23961],{"id":23960},"learn-more","Learn More",[15,23963,23964],{},"We will be publishing follow-up blog posts with more details, best practices and examples on how to use Node-RED to make sense of your industrial data. In the meantime, you can learn more about these tools by visiting the following links:",[39,23966,23967,23973,23980],{},[42,23968,23969],{},[63,23970,23972],{"href":23971},"\u002Fblog\u002Fnode-red\u002F","Node-RED blog posts",[42,23974,23975],{},[63,23976,23979],{"href":23977,"rel":23978},"https:\u002F\u002Fwww.youtube.com\u002Fplaylist?list=PLpcyqc7kNgp09XeRx_cae1fEIOloPqM1C",[67],"Node-RED videos",[42,23981,23982],{},[63,23983,23986],{"href":23984,"rel":23985},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-buffer-parser",[67],"Buffer Parser Node",{"title":103,"searchDepth":104,"depth":104,"links":23988},[23989,23990,23996,23997,23998,24003,24004],{"id":23778,"depth":294,"text":23779},{"id":23785,"depth":294,"text":23786,"children":23991},[23992,23993,23994,23995],{"id":23792,"depth":104,"text":23793},{"id":23807,"depth":104,"text":23808},{"id":23820,"depth":104,"text":23821},{"id":23834,"depth":104,"text":23835},{"id":23848,"depth":294,"text":23849},{"id":23887,"depth":294,"text":23888},{"id":23897,"depth":294,"text":23898,"children":23999},[24000,24001,24002],{"id":23904,"depth":104,"text":23905},{"id":23915,"depth":104,"text":23916},{"id":23930,"depth":104,"text":23931},{"id":23941,"depth":294,"text":23942},{"id":23960,"depth":294,"text":23961},"2023-09-14","Learn how to bridge legacy industrial protocols like Modbus to the IIoT era using Node-RED and buffer parsing. Explore data types, conversion challenges, and examples",{"excerpt":24008},{"type":12,"value":24009},[24010,24012],[15,24011,23772],{},[15,24013,23775],{},"\u002Fblog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data",{"title":23766,"description":24006},{"loc":24014},"blog\u002F2023\u002F09\u002Fmodernize-your-legacy-industrial-data","Working with legacy industrial protocol data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world.",[8650,138,137],"lKIxsrZebPKdRtWJTHzR9qQw5_cmAPALoVZ273wcKAk",{"id":24022,"title":24023,"authors":24024,"body":24026,"cta":25423,"date":25426,"description":25427,"extension":116,"image":25428,"lastUpdated":6203,"meta":25429,"navigation":129,"path":25439,"seo":25440,"sitemap":25441,"stem":25442,"subtitle":25443,"tags":25444,"tldr":25445,"video":3,"__hash__":25446},"blog\u002Fblog\u002F2023\u002F07\u002Fhow-to-build-a-opc-client-dashboard-in-node-red.md","How to Build an OPC UA Client Dashboard in Node-RED - Part 3 (2026)",[24025],"richard-meyer",{"type":12,"value":24027,"toc":25412},[24028,24039,24047,24050,24054,24061,24080,24083,24090,24094,24106,24109,24115,24121,24139,24142,24146,24149,24163,24166,24170,24173,24179,24182,24322,24328,24335,24345,24352,24358,24361,24367,24370,24376,24394,24400,24407,24413,24427,24446,24449,24454,24457,24468,24472,24475,24482,24704,24713,24723,24741,24760,24770,24777,24795,24802,24808,24815,24822,24825,24831,24835,24838,24845,24871,24887,24890,24904,24914,24928,24945,24948,24954,24958,24961,24967,25027,25041,25057,25072,25079,25093,25096,25099,25105,25109,25112,25118,25131,25133,25136,25139,25142],[15,24029,24030,24031,24034,24035,24038],{},"This article is the third and final part of our OPC UA content series. In the ",[63,24032,24033],{"href":11759},"first article",", we cover some OPC UA fundamentals and walk through an example OPC UA Server flow. In the ",[63,24036,24037],{"href":2640},"second article",", we built a SSL-secured OPC UA server using data from an Allen Bradley PLC as a source.\nIn this article, we show how to build an OPC Client in Node-RED that communicates with a 3rd party OPC UA Server and utilizes an interactive dashboard.",[15,24040,24041,24042,24046],{},"This article will requires the ",[63,24043,24045],{"href":11357,"rel":24044},[67],"Prosys OPC UA Simulation Server",", an application designed for testing OPC UA client applications and learning the technology.  It’s a free cross-platform application that supports Windows, Linux, and MacOS.  This article will use the Windows version.",[15,24048,24049],{},"Note: full source code for the OPC Client Dashboard is included at the end of the article.",[31,24051,24053],{"id":24052},"custom-nodes-used-assumptions","Custom Nodes Used & Assumptions",[15,24055,24056,24057,24060],{},"Several custom nodes are required in order to properly deploy this flow.  For more detailed information on how to install a custom node, follow the instructions from an ",[63,24058,24059],{"href":11766},"earlier article"," where the process on installing custom nodes is explained in detail.",[39,24062,24063,24069,24074],{},[42,24064,24065],{},[63,24066,1536],{"href":24067,"rel":24068},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002F@flowfuse\u002Fnode-red-dashboard",[67],[42,24070,24071],{},[63,24072,11368],{"href":11366,"rel":24073},[67],[42,24075,24076],{},[63,24077,7333],{"href":24078,"rel":24079},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002F@flowfuse\u002Fnode-red-dashboard-2-ui-led",[67],[15,24081,24082],{},"As this is not a production application, no security will be utilized, and it is assumed that the OPC UA Server is running on the same network as the Node-RED OPC Client.",[15,24084,24085,24086,29],{},"Is it also assumed that the end user of this article has familiarization with dashboards.  There are many dashboard basic guides available on our FlowFuse website, For more infomation go to ",[63,24087,24089],{"href":24088},"\u002Fblog\u002Fdashboard\u002F","Node-RED Dashboard 2.0 guides",[31,24091,24093],{"id":24092},"install-and-deploy-the-prosys-opc-ua-simulation-server","Install and Deploy the Prosys OPC UA Simulation Server",[15,24095,24096,24097,24102,24103,29],{},"The Prosys OPC UA Simulation Server is ",[63,24098,24101],{"href":24099,"rel":24100},"https:\u002F\u002Fprosysopc.com\u002Fproducts\u002Fopc-ua-simulation-server\u002Fevaluate\u002F",[67],"free to download",", but requires a sign-up process.  Download and install the server, then run the application.  Once the application is started, the first thing you should do is go to ",[924,24104,24105],{},"options -> switch to expert mode",[15,24107,24108],{},"This will give us access to the address space tab, which we will need to develop our client application in Node-RED.",[15,24110,24111],{},[165,24112],{"alt":24113,"dataZoomable":103,"src":24114},"expert-mode.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fexpert-mode.png",[15,24116,24117,24118,24120],{},"When the application is run, an endpoint url will be displayed on the ",[924,24119,977],{}," tab, along with an indication that the server is currently running.",[15,24122,24123,24127,24128,24131,24132,24135,24136,29],{},[165,24124],{"alt":24125,"dataZoomable":103,"src":24126},"opc-endpoint-url.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-endpoint-url.png","\nCopy the connection endpoint, but be warned that you will likely need to replace the computer name (in my case ",[924,24129,24130],{},"DESKTOP-0K0483A",", with the actual IP address of the machine running the server.  The IP address of the machine on my local network is ",[924,24133,24134],{},"192.168.0.141",", which changes my UA TCP endpoint address to ",[924,24137,24138],{},"opc.tcp:\u002F\u002F192.168.0.141:53530\u002FOPCUA\u002FSimulationServer",[15,24140,24141],{},"Now the simulation server is set up and we are ready to start developing the OPC Client application.",[31,24143,24145],{"id":24144},"objectives-of-the-node-red-opc-client-dashboard-application","Objectives of the Node-RED OPC Client Dashboard Application",[15,24147,24148],{},"The goal is not to develop a production-level application, rather, it’s to show a variety of features that one can utilize to demonstrate common OPC UA Client application capabilities in Node-RED, while also demonstrating a variety of methods to visualize the results in a dashboard.  There are 4 main objectives of the Node-RED OPC Client Dashboard application. They are:",[271,24150,24151,24154,24157,24160],{},[42,24152,24153],{},"Browse hierarchical server address space structure & display on a dashboard",[42,24155,24156],{},"Read OPC UA values from various namespaces, showing a variety of datatypes and different ways they can be visualized",[42,24158,24159],{},"Write OPC UA values back to the OPC UA server directly from the OPC UA Client dashboard",[42,24161,24162],{},"Read alarms & events from the OPC UA Server and display them on the dashboard",[15,24164,24165],{},"Rather than building the flow step-by-step, the flow source code will be presented for each objective, and a the flow will be explained so that it is understood what is happening in each section of code.",[31,24167,24169],{"id":24168},"browse-hierarchical-server-address-space-structure-with-opc-ua-browser-node","Browse Hierarchical Server Address Space Structure With OPC UA Browser Node",[15,24171,24172],{},"The first flow will browse the hierarchical OPC UA Server address space structure and display it on the dashboard.",[15,24174,24175],{},[165,24176],{"alt":24177,"dataZoomable":103,"src":24178},"image-20230727-085611.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fimage-20230727-085611.png",[15,24180,24181],{},"You can import this flow into Node-RED using the code below:",[15,24183,4917,24184,1150],{},[1107,24185,24186,24187,24190,24191,24193,24194,24196,24197,24206,24207,24209,24210,24218,24219,24227,24228,24230,24231,24234,24235,24242,24243,24251,24252,24259,24260,24263,24264,24266,24267,24269,24270,24272,24273,24283,24284,24292,24293,24302,24303,24308,24309,24313,24314,24318,24319,24321],{},"{\"id\":\"ca62be3e01388319\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Browse Hierarchical Address Space Structure & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,24188,24189],{},"\"6b17b2da2b942bb4\",\"61797eccf2785257\",\"4d92d940177b6ee3\",\"68a113d5893b7c01\",\"d0c969b6a59fac3a\",\"639da01fc957e547\",\"29437ca7222d9a64\",\"49983d5da0958bf2\",\"49040d0cf1144f0a\",\"e7c55f412ef86543\",\"de21b7ad98a05833\",\"2d56e9a431c21a3b\",\"ac95bd0e2b304eec\",\"6fdabcc2950ccf4e\",\"1c49fa5142d2cf17\",\"335878527020598c\",\"7b208f2e8cba6205\",\"52dd2e5dcddad58f\",\"a5acdccfd2033aec\",\"157322c9c360446d\",\"78a012e5db377fd9\"",",\"x\":94,\"y\":139,\"w\":1172,\"h\":422},{\"id\":\"6b17b2da2b942bb4\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24192],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":550,\"y\":280,\"wires\":[[\"4d92d940177b6ee3\",\"d0c969b6a59fac3a\",\"639da01fc957e547\"]]},{\"id\":\"61797eccf2785257\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Get Base Folder Structure\",\"props\":",[1107,24195,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":\"0.3\",\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":280,\"y\":280,\"wires\":[[\"6b17b2da2b942bb4\"]]},{\"id\":\"4d92d940177b6ee3\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Simulation Folder\",\"rules\":",[1107,24198,24199,24200,24202,24203,24205],{},"{\"t\":\"set\",\"p\":\"Objects.Simulation.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24201,4948],{},".item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload",[1107,24204,4948],{},".item.browseName.name\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":220,\"wires\":[[\"335878527020598c\"]]},{\"id\":\"68a113d5893b7c01\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1140,\"y\":180,\"wires\":",[1107,24208],{},"},{\"id\":\"d0c969b6a59fac3a\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyObjects Folder\",\"rules\":",[1107,24211,24212,24213,24202,24216,24205],{},"{\"t\":\"set\",\"p\":\"Objects.MyObjects.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24214,24215],{},"4",[1107,24217,24215],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":340,\"wires\":[[\"52dd2e5dcddad58f\"]]},{\"id\":\"639da01fc957e547\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticData Folder\",\"rules\":",[1107,24220,24221,24222,24202,24225,24205],{},"{\"t\":\"set\",\"p\":\"Objects.StaticData.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24223,24224],{},"3",[1107,24226,24224],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":280,\"wires\":[[\"7b208f2e8cba6205\"]]},{\"id\":\"29437ca7222d9a64\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24229],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":550,\"y\":440,\"wires\":[[\"49040d0cf1144f0a\",\"e7c55f412ef86543\"]]},{\"id\":\"49983d5da0958bf2\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Get StaticData Folder Structure\",\"props\":",[1107,24232,24233],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.StaticData.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":\"0.3\",\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":270,\"y\":440,\"wires\":[[\"29437ca7222d9a64\"]]},{\"id\":\"49040d0cf1144f0a\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"AnalogItemArrays Folder\",\"rules\":",[1107,24236,24237,24238,24202,24240,24205],{},"{\"t\":\"set\",\"p\":\"Objects.StaticData.AnalogItemArrays.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24239,4944],{},[1107,24241,4944],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":850,\"y\":460,\"wires\":[[\"157322c9c360446d\"]]},{\"id\":\"e7c55f412ef86543\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticArrayVariables Folder\",\"rules\":",[1107,24244,24245,24246,24202,24249,24205],{},"{\"t\":\"set\",\"p\":\"Objects.StaticData.StaticArrayVariables.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24247,24248],{},"6",[1107,24250,24248],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":860,\"y\":400,\"wires\":[[\"a5acdccfd2033aec\"]]},{\"id\":\"de21b7ad98a05833\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyDevice Object\",\"rules\":",[1107,24253,24254,24255,24202,24257,24205],{},"{\"t\":\"set\",\"p\":\"Objects.MyObjects.MyDevice.nodeId\",\"pt\":\"flow\",\"to\":\"payload",[1107,24256,1855],{},[1107,24258,1855],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":520,\"wires\":[[\"78a012e5db377fd9\"]]},{\"id\":\"2d56e9a431c21a3b\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Get MyObjects Object Structure\",\"props\":",[1107,24261,24262],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.MyObjects.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":\"0.5\",\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":270,\"y\":520,\"wires\":[[\"ac95bd0e2b304eec\"]]},{\"id\":\"ac95bd0e2b304eec\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24265],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":550,\"y\":520,\"wires\":[[\"de21b7ad98a05833\"]]},{\"id\":\"6fdabcc2950ccf4e\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Store & Parse nodeId & browseName\",\"info\":\"\",\"x\":850,\"y\":180,\"wires\":",[1107,24268],{},"},{\"id\":\"1c49fa5142d2cf17\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Global Address Space Folder Browse\",\"info\":\"\",\"x\":410,\"y\":220,\"wires\":",[1107,24271],{},"},{\"id\":\"335878527020598c\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"Simulation\",\"order\":1,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24274,24275,24276,24279,24280,24282],{},"\\n    \u003Cdiv class=\"inline-content\">\\n        ",[15,24277,24278],{},"Namespace 3","\\n        \u003Cv-icon color=\"black\" icon=\"mdi-folder\" size=\"large\"> ",[2526,24281],{"value":1227},"\\n    \\n","\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1110,\"y\":220,\"wires\":[[]]},{\"id\":\"7b208f2e8cba6205\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"StaticData\",\"order\":2,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24285,24275,24286,24289,24290,24282],{},[15,24287,24288],{},"Namespace 5","\\n        \u003Cv-icon color=\"black\" icon=\"mdi-folder-arrow-down\" size=\"large\"> ",[2526,24291],{"value":1227},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1110,\"y\":280,\"wires\":[[]]},{\"id\":\"52dd2e5dcddad58f\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"MyObjects\",\"order\":5,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24294,24295,24296,24299,24300,24282],{},"\\n    ",[15,24297,24298],{},"Namespace 6","\\n    \u003Cdiv class=\"inline-content\">\\n        \u003Cv-icon color=\"black\" icon=\"mdi-folder\" size=\"large\"> ",[2526,24301],{"value":1227},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1110,\"y\":340,\"wires\":[[]]},{\"id\":\"a5acdccfd2033aec\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"StaticArrayVariables\",\"order\":3,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24304,24305,24306,24282],{},"\\n    \u003Cdiv class=\"d-flex align-center ml-3\">\\n        \u003Cv-icon color=\"black\" icon=\"mdi-folder\" size=\"large\"> ",[2526,24307],{"value":1227},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1140,\"y\":400,\"wires\":[[]]},{\"id\":\"157322c9c360446d\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"AnalogItemArrays\",\"order\":4,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24310,24305,24311,24282],{},[2526,24312],{"value":1227},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1130,\"y\":460,\"wires\":[[]]},{\"id\":\"78a012e5db377fd9\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"MyDevice\",\"order\":6,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,24315,24305,24316,24282],{},[2526,24317],{"value":1227},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1100,\"y\":520,\"wires\":[[]]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp:\u002F\u002F192.168.56.1:53530\u002FOPCUA\u002FSimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"ef9998baf5f61e8a\",\"type\":\"ui-group\",\"name\":\" Address Space Folder Structure\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"2\",\"height\":\"1\",\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"44d3feb2a1143d7b\",\"type\":\"ui-page\",\"name\":\"OPC UA\",\"ui\":\"5355e0c476f9da3b\",\"path\":\"\u002Fopcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,24320,5343],{},",\"showPathInSidebar\":false,\"navigationStyle\":\"default\"},{\"id\":\"61eee6fc60281b9b\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#0094ce\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}}",[15,24323,24324,24325,9430],{},"To understand what is going on in this flow, we must refer back to the OPC UA Simulation Server ",[924,24326,24327],{},"Address Space",[15,24329,24330,24331,24334],{},"When we browse the OPC Server base folder structure in Node-RED, we will be browsing everything included under the ",[924,24332,24333],{},"Objects"," tree.",[15,24336,24337,24341,24342,24344],{},[165,24338],{"alt":24339,"dataZoomable":103,"src":24340},"address-space.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Faddress-space.png","\nIn our flow, we get the base folder structure by using an OPC-UA Browser node, as shown, with an endpoint that points to our OPC UA Server endpoint url we grabbed earlier in this article.  It is also worth noting we leave the ",[924,24343,8623],{}," blank.  By doing this, we will browse the entire folder structure by default.",[15,24346,24347,24351],{},[165,24348],{"alt":24349,"dataZoomable":103,"src":24350},"image-20230727-090252.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fimage-20230727-090252.png","\nThe configuration of the endpoint properties includes no security credentials, as shown below.",[15,24353,24354],{},[165,24355],{"alt":24356,"dataZoomable":103,"src":24357},"endpoint-configure.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fendpoint-configure.png",[15,24359,24360],{},"Using the output of a debug node, we get from the OPC UA Browser yield a payload with an array of 5 objects.",[15,24362,24363],{},[165,24364],{"alt":24365,"dataZoomable":103,"src":24366},"address-debug.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Faddress-debug.png",[15,24368,24369],{},"Each object returned represents the 5 objects that are in our OPC UA Server Objects tree.",[15,24371,24372],{},[165,24373],{"alt":24374,"dataZoomable":103,"src":24375},"browse-payload-1.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fbrowse-payload-1.png",[15,24377,24378,24379,853,24382,4972,24385,24388,24389,1413,24392,29],{},"However, of those 5 objects, only 3 of them are folders that contain actual OPC values.  ",[924,24380,24381],{},"MyObjects",[924,24383,24384],{},"Simulation",[924,24386,24387],{},"StaticData",".  We can ignore ",[924,24390,24391],{},"Aliases",[924,24393,14637],{},[15,24395,24396],{},[165,24397],{"alt":24398,"dataZoomable":103,"src":24399},"address-space-folders-only.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Faddress-space-folders-only.png",[15,24401,24402,24403,24406],{},"So looking deeper into the payload of our global browse from the ",[924,24404,24405],{},"OPC UA Browser node",", we can drill down into the details and see how they correlate with the folders in the server.",[15,24408,24409],{},[165,24410],{"alt":24411,"dataZoomable":103,"src":24412},"browse-node.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fbrowse-node.png",[15,24414,24415,24416,24418,24419,24422,24423,24426],{},"As shown above, element 2 in the array returned from the global browse corresponds to the ",[924,24417,24384],{}," folder.  And we are interested in two important values in this data-structure - the ",[924,24420,24421],{},"NodeId",", which is topic an OPC Client uses to point specific OPC values, and the ",[924,24424,24425],{},"browseName",", which is the name we see visually when we try to identify an OPC topic.  We can now use this logic to parse out this useful information using a change node.",[15,24428,24429,24433,24434,1413,24437,24439,24440,24442,24443,24445],{},[165,24430],{"alt":24431,"dataZoomable":103,"src":24432},"simulation-folder.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fsimulation-folder.png","\nThis change node is grabbing the ",[924,24435,24436],{},"nodeId",[924,24438,24425],{}," .  The ",[924,24441,24436],{}," is stored in a context variable for later use, while the ",[924,24444,24425],{}," is used as the payload to be displayed on our dashboard.",[15,24447,24448],{},"The rest of the flow follows this same pattern, to end up with a folder structure that we can display on our dashboard that matches the structure on our OPC Server",[39,24450,24451],{},[42,24452,24453],{},"note - to make the flow more manageable, not all browsable folders were included in the dashboard, as this flow is just meant to serve as an example, rather than be a 1:1 copy of everything in the server.",[15,24455,24456],{},"If you deploy the flow and pull up the dashboard, it results in the following output -",[15,24458,24459,24463,24464,24467],{},[165,24460],{"alt":24461,"dataZoomable":103,"src":24462},"address-space-dashboard.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Faddress-space-dashboard.png","\nShowing side-by-side with the server, you can see that we successfully browsed a portion of the address space and displayed the values on the dashboard.  Admittedly, a lot of work for not much pay-off, but it’s a worthwhile exercise in understanding how to browse topics using the ",[924,24465,24466],{},"OPC UA Browser"," node.  The browser node is best used for reading OPC UA values, which will be covered next.",[31,24469,24471],{"id":24470},"read-opc-ua-values-using-opc-ua-browser-node","Read OPC UA Values Using OPC UA Browser Node",[15,24473,24474],{},"The next set of flows read OPC UA values from the server and displays them on the dashboard.",[15,24476,24477,24481],{},[165,24478],{"alt":24479,"dataZoomable":103,"src":24480},"read-opc-values.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fread-opc-values.png","\nYou can import these flows into Node-RED using the code below:",[15,24483,4917,24484,1150],{},[1107,24485,24186,24486,24190,24488,24193,24490,24196,24492,24206,24498,24209,24500,24218,24506,24227,24512,24230,24514,24234,24516,24242,24522,24251,24528,24259,24534,24263,24536,24266,24538,24269,24540,24272,24542,24283,24548,24292,24554,24302,24560,24308,24564,24313,24568,24318,24572,24574,24575,24578,24579,24581,24582,24585,24586,24588,24589,24595,24596,24599,24600,24604,24605,24609,24610,24614,24615,24617,24618,24620,24621,24623,24624,24627,24628,24630,24631,24633,24634,24636,24637,24640,24641,24643,24644,24647,24648,24650,24651,24654,24655,24657,24658,24664,24665,24667,24668,24670,24671,24674,24675,24679,24680,24682,24683,24685,24686,24688,24689,24691,24692,24694,24695,24697,24698,24700,24701,24703],{},[1107,24487,24189],{},[1107,24489],{},[1107,24491,1132],{},[1107,24493,24199,24494,24202,24496,24205],{},[1107,24495,4948],{},[1107,24497,4948],{},[1107,24499],{},[1107,24501,24212,24502,24202,24504,24205],{},[1107,24503,24215],{},[1107,24505,24215],{},[1107,24507,24221,24508,24202,24510,24205],{},[1107,24509,24224],{},[1107,24511,24224],{},[1107,24513],{},[1107,24515,24233],{},[1107,24517,24237,24518,24202,24520,24205],{},[1107,24519,4944],{},[1107,24521,4944],{},[1107,24523,24245,24524,24202,24526,24205],{},[1107,24525,24248],{},[1107,24527,24248],{},[1107,24529,24254,24530,24202,24532,24205],{},[1107,24531,1855],{},[1107,24533,1855],{},[1107,24535,24262],{},[1107,24537],{},[1107,24539],{},[1107,24541],{},[17139,24543,24275,24544,24279,24546,24282],{},[15,24545,24278],{},[2526,24547],{"value":1227},[17139,24549,24275,24550,24289,24552,24282],{},[15,24551,24288],{},[2526,24553],{"value":1227},[17139,24555,24295,24556,24299,24558,24282],{},[15,24557,24298],{},[2526,24559],{"value":1227},[17139,24561,24305,24562,24282],{},[2526,24563],{"value":1227},[17139,24565,24305,24566,24282],{},[2526,24567],{"value":1227},[17139,24569,24305,24570,24282],{},[2526,24571],{"value":1227},[1107,24573,5343],{},",\"showPathInSidebar\":false,\"navigationStyle\":\"default\"},{\"id\":\"61eee6fc60281b9b\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#0094ce\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"8557072f05e4bda0\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read Simulation Values & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,24576,24577],{},"\"9659d40ac9063764\",\"9f5b597ec8179fb4\",\"a8d919f497fcff04\",\"13f5c98b7fd5f5da\",\"ec5dca5eb9d4971b\",\"1780cb86597d3c67\",\"1a2fcac87247cda4\",\"4d9b758e39555124\",\"da468bc150517fa6\",\"82aa12173dd7bbca\",\"57d8777e34b55b7b\",\"10877909d1daf6fe\",\"c4d4a3b0df372e4c\",\"b0cf511f824f2a86\",\"f2efc6b419414c9a\"",",\"x\":94,\"y\":599,\"w\":1372,\"h\":302},{\"id\":\"9659d40ac9063764\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24580],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":570,\"y\":760,\"wires\":[[\"ec5dca5eb9d4971b\"]]},{\"id\":\"9f5b597ec8179fb4\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Update Simulation Values @ 1 second\",\"props\":",[1107,24583,24584],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.Simulation.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"1\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":300,\"y\":760,\"wires\":[[\"9659d40ac9063764\"]]},{\"id\":\"a8d919f497fcff04\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Read Simulation Values\",\"info\":\"\",\"x\":460,\"y\":720,\"wires\":",[1107,24587],{},"},{\"id\":\"13f5c98b7fd5f5da\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Counter Value\",\"rules\":",[1107,24590,24591,24592,24594],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload",[1107,24593,4944],{},".item.value\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1070,\"y\":680,\"wires\":[[\"10877909d1daf6fe\"]]},{\"id\":\"ec5dca5eb9d4971b\",\"type\":\"switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"empty check\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":",[1107,24597,24598],{},"{\"t\":\"nempty\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":790,\"y\":760,\"wires\":[[\"13f5c98b7fd5f5da\",\"1780cb86597d3c67\",\"1a2fcac87247cda4\",\"4d9b758e39555124\"]]},{\"id\":\"1780cb86597d3c67\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Random Number Value\",\"rules\":",[1107,24601,24591,24602,24594],{},[1107,24603,4948],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1100,\"y\":740,\"wires\":[[\"c4d4a3b0df372e4c\"]]},{\"id\":\"1a2fcac87247cda4\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Sawtooth Value\",\"rules\":",[1107,24606,24591,24607,24594],{},[1107,24608,24224],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1080,\"y\":800,\"wires\":[[\"b0cf511f824f2a86\"]]},{\"id\":\"4d9b758e39555124\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Sawtooth Value\",\"rules\":",[1107,24611,24591,24612,24594],{},[1107,24613,24215],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1080,\"y\":860,\"wires\":[[\"f2efc6b419414c9a\"]]},{\"id\":\"da468bc150517fa6\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Discard Empty Datasets\",\"info\":\"\",\"x\":780,\"y\":720,\"wires\":",[1107,24616],{},"},{\"id\":\"82aa12173dd7bbca\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Parse Simulation Values\",\"info\":\"\",\"x\":1070,\"y\":640,\"wires\":",[1107,24619],{},"},{\"id\":\"57d8777e34b55b7b\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":640,\"wires\":",[1107,24622],{},"},{\"id\":\"10877909d1daf6fe\",\"type\":\"ui-gauge\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Counter\",\"group\":\"af263064820fb7d0\",\"order\":0,\"width\":3,\"height\":3,\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"gauge\",\"units\":\"units\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":",[1107,24625,24626],{},"{\"from\":\"0\",\"color\":\"#5cd65c\"},{\"from\":\"15\",\"color\":\"#ffc800\"},{\"from\":\"30\",\"color\":\"#ea5353\"}",",\"min\":0,\"max\":\"30\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":1320,\"y\":680,\"wires\":",[1107,24629],{},"},{\"id\":\"c4d4a3b0df372e4c\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"group\":\"af263064820fb7d0\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"Random Number\",\"label\":\"Random Number\",\"format\":\"",[2526,24632],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1350,\"y\":740,\"wires\":",[1107,24635],{},"},{\"id\":\"b0cf511f824f2a86\",\"type\":\"ui-chart\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"group\":\"af263064820fb7d0\",\"name\":\"\",\"label\":\"Sawtooth\",\"order\":9007199254740991,\"chartType\":\"line\",\"category\":\"Sawtooth\",\"categoryType\":\"str\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"time\",\"yAxisProperty\":\"\",\"ymin\":\"\",\"ymax\":\"\",\"action\":\"append\",\"pointShape\":\"line\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"60\",\"removeOlderPoints\":\"\",\"colors\":",[1107,24638,24639],{},"\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"",",\"width\":\"3\",\"height\":\"4\",\"className\":\"\",\"x\":1320,\"y\":800,\"wires\":[[]]},{\"id\":\"f2efc6b419414c9a\",\"type\":\"ui-chart\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"group\":\"af263064820fb7d0\",\"name\":\"\",\"label\":\"Sinusoid\",\"order\":9007199254740991,\"chartType\":\"line\",\"category\":\"Sawtooth\",\"categoryType\":\"str\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"time\",\"yAxisProperty\":\"\",\"ymin\":\"\",\"ymax\":\"\",\"action\":\"append\",\"pointShape\":\"line\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"60\",\"removeOlderPoints\":\"\",\"colors\":",[1107,24642,24639],{},",\"width\":\"3\",\"height\":\"4\",\"className\":\"\",\"x\":1320,\"y\":860,\"wires\":[[]]},{\"id\":\"af263064820fb7d0\",\"type\":\"ui-group\",\"name\":\"Simulation values\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"3\",\"height\":\"1\",\"order\":2,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5afdbddf71507886\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read StaticData Values & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,24645,24646],{},"\"e998aa804042128b\",\"6c9b7d4d195a1e9a\",\"cd097b744d0ec625\",\"18d21607c87ab153\",\"7b5143c4960f92a1\",\"0625b0cf6f546a4a\",\"9d899fbb4d1648b3\",\"6e1edc31687dde54\",\"051e1f282076fed2\",\"de2a1c3e380f743b\",\"c74606c48ccf5a40\",\"053bda13f2a2eabe\",\"277dcf430dc86996\",\"d708e6264cec0070\"",",\"x\":84,\"y\":939,\"w\":1382,\"h\":202},{\"id\":\"e998aa804042128b\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24649],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":630,\"y\":1020,\"wires\":[[\"7b5143c4960f92a1\"]]},{\"id\":\"6c9b7d4d195a1e9a\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Update AnalogItemArrays Values @ 1 second\",\"props\":",[1107,24652,24653],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.StaticData.AnalogItemArrays.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"1\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":320,\"y\":1020,\"wires\":[[\"e998aa804042128b\"]]},{\"id\":\"cd097b744d0ec625\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Read StaticData Values\",\"info\":\"\",\"x\":520,\"y\":980,\"wires\":",[1107,24656],{},"},{\"id\":\"18d21607c87ab153\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Get ByteAnalogItemArray Value\",\"rules\":",[1107,24659,24660,24661,24663],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$string(payload",[1107,24662,1855],{},".item.value)\\t\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1070,\"y\":1020,\"wires\":[[\"277dcf430dc86996\"]]},{\"id\":\"7b5143c4960f92a1\",\"type\":\"switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"empty check\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":",[1107,24666,24598],{},",\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":830,\"y\":1020,\"wires\":[[\"18d21607c87ab153\"]]},{\"id\":\"0625b0cf6f546a4a\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,24669],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":630,\"y\":1100,\"wires\":[[\"051e1f282076fed2\"]]},{\"id\":\"9d899fbb4d1648b3\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Update StaticArrayVariables Values @1 second\",\"props\":",[1107,24672,24673],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.StaticData.StaticArrayVariables.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"1\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":320,\"y\":1100,\"wires\":[[\"0625b0cf6f546a4a\"]]},{\"id\":\"6e1edc31687dde54\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Get BooleanArray Value\",\"rules\":",[1107,24676,24591,24677,24594],{},[1107,24678,1855],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1050,\"y\":1100,\"wires\":[[\"d708e6264cec0070\"]]},{\"id\":\"051e1f282076fed2\",\"type\":\"switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"empty check\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":",[1107,24681,24598],{},",\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":830,\"y\":1100,\"wires\":[[\"6e1edc31687dde54\"]]},{\"id\":\"de2a1c3e380f743b\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Discard Empty Datasets\",\"info\":\"\",\"x\":820,\"y\":980,\"wires\":",[1107,24684],{},"},{\"id\":\"c74606c48ccf5a40\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Parse StaticData Values\",\"info\":\"\",\"x\":1070,\"y\":980,\"wires\":",[1107,24687],{},"},{\"id\":\"053bda13f2a2eabe\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":980,\"wires\":",[1107,24690],{},"},{\"id\":\"277dcf430dc86996\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"ByteAnalogItemArray\",\"format\":\"",[2526,24693],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1340,\"y\":1020,\"wires\":",[1107,24696],{},"},{\"id\":\"d708e6264cec0070\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"BooleanArray\",\"format\":\"",[2526,24699],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1320,\"y\":1100,\"wires\":",[1107,24702],{},"},{\"id\":\"3d4f386e812e8b5f\",\"type\":\"ui-group\",\"name\":\"StaticData Values\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"4\",\"height\":\"1\",\"order\":3,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"}",[15,24705,24706,24707,24709,24710,24712],{},"The values are derived from the ",[924,24708,24436],{}," values we stored in memory in our previous flow, via our ",[924,24711,9518],{}," nodes in the previous flow.",[15,24714,24715,24719,24720,24722],{},[165,24716],{"alt":24717,"dataZoomable":103,"src":24718},"flow-context-nodeid.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fflow-context-nodeid.png","\nAs stated earlier, you reference a OPC UA topic by its ",[924,24721,24436],{},".  So we will use these node IDs to read actual values from our OPC nodes.",[15,24724,24725,24726,24728,24729,24731,24732,24734,24735,24737,24738,24740],{},"In our first flow, we want to read the values in the ",[924,24727,24384],{}," folder at a 1 second interval.  So we use an ",[924,24730,1588],{}," node with a ",[924,24733,1241],{}," that references the ",[924,24736,24436],{}," corresponding to the ",[924,24739,24384],{}," folder.",[15,24742,24743,24747,24748,24750,24751,24753,24754,24756,24757,24759],{},[165,24744],{"alt":24745,"dataZoomable":103,"src":24746},"simulation-injection.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fsimulation-injection.png","\nThat ",[924,24749,1241],{}," tells the ",[924,24752,24466],{}," node what ",[924,24755,24436],{}," to browse.  If we look at the debug output of the browser ",[924,24758,1227],{},", we can see that it produces an array of 7 objects, and an empty set array.",[15,24761,24762,24766,24767,24769],{},[165,24763],{"alt":24764,"dataZoomable":103,"src":24765},"simulation-debug.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fsimulation-debug.png","\nIf we allow that empty array to be passed, that means all values will be reset to 0 on each read.  So to prevent that from happening, we use a ",[924,24768,9482],{}," node to filter out the empty set.",[15,24771,24772,24776],{},[165,24773],{"alt":24774,"dataZoomable":103,"src":24775},"empty-check.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fempty-check.png","\nNow only non-empty payloads will be passed, preventing the values being reset to 0 on each read.",[15,24778,24779,24780,24782,24783,24785,24786,24788,24789,24792,24793,24740],{},"Now we can actually read the values.  To do this, we use a ",[924,24781,9518],{}," node again, referencing the non-empty payload and drilling down to the ",[924,24784,10563],{}," that corresponds to the ",[924,24787,1914],{}," of the node we want to read.  In this case, we’re getting the value of the node ",[924,24790,24791],{},"Counter"," located in the ",[924,24794,24384],{},[15,24796,24797,24801],{},[165,24798],{"alt":24799,"dataZoomable":103,"src":24800},"get-counter-value.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fget-counter-value.png","\nGoing back to our OPC Server, we can see that exactly where that value is derived below -",[15,24803,24804],{},[165,24805],{"alt":24806,"dataZoomable":103,"src":24807},"sim-counter-server.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fsim-counter-server.png",[15,24809,24810,24811,24814],{},"Now we add a ",[924,24812,24813],{},"gauge"," dashboard node to visualize the counter on the dashboard.  In the OPC Server, it is shown that the counter increments in a range of 0-30 in 1 count increments.",[15,24816,24817,24821],{},[165,24818],{"alt":24819,"dataZoomable":103,"src":24820},"counter-properties.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fcounter-propertie.png","\nNow that we’ve gone through the full process of reading an OPC UA value and displaying it on the dashboard, we can apply the same logic other values published by the OPC UA Server, which are repeated in the remaining parts of the flow.",[15,24823,24824],{},"The end result on the dashboard now looks like this -",[15,24826,24827],{},[165,24828],{"alt":24829,"dataZoomable":103,"src":24830},"opc-read-dashboard.gif","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-read-dashboard.gif",[31,24832,24834],{"id":24833},"write-opc-ua-values-to-server-using-opcua-item-and-opc-ua-client-nodes","Write OPC UA Values To Server Using OpcUa-Item and Opc-Ua-Client Nodes",[15,24836,24837],{},"The next flow writes OPC UA values to the server using dashboard UI elements.",[15,24839,24840,24844],{},[165,24841],{"alt":24842,"dataZoomable":103,"src":24843},"write-mydevice.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fwrite-mydevice.png","\nYou can import this flow into Node-RED using the code below:",[15,24846,4917,24847,1150],{},[1107,24848,24849,24850,24853,24854,24856,24857,24859,24860,24862,24863,24865,24866,24868,24869,24321],{},"{\"id\":\"3de6c861611c3afa\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Write Mydevices values to OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,24851,24852],{},"\"a66583d91b581cd8\",\"3e8cb6e199012155\",\"9fa33d1c9c621611\",\"fb7f57b4da5883ae\",\"9c5ff104eb9c8b10\",\"77bcb828bec95336\",\"afa83dbb46449d4a\",\"fa08f0ed04296363\",\"9f591797b56c565d\"",",\"x\":94,\"y\":1439,\"w\":792,\"h\":182},{\"id\":\"a66583d91b581cd8\",\"type\":\"OpcUa-Item\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"item\":\"ns=6;s=MySwitch\",\"datatype\":\"Boolean\",\"value\":\"\",\"name\":\"Toggle MySwitch\",\"x\":470,\"y\":1520,\"wires\":[[\"3e8cb6e199012155\"]]},{\"id\":\"3e8cb6e199012155\",\"type\":\"OpcUa-Client\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"endpoint\":\"53f4394dbf12c6b7\",\"action\":\"write\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"Write MySwitch\",\"x\":720,\"y\":1520,\"wires\":[[],",[1107,24855],{},"]},{\"id\":\"9fa33d1c9c621611\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Dashboard Input\",\"info\":\"\",\"x\":200,\"y\":1480,\"wires\":",[1107,24858],{},"},{\"id\":\"fb7f57b4da5883ae\",\"type\":\"OpcUa-Item\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"item\":\"ns=6;s=MyLevel\",\"datatype\":\"Double\",\"value\":\"\",\"name\":\"Modify MyLevel\",\"x\":460,\"y\":1580,\"wires\":[[\"9c5ff104eb9c8b10\"]]},{\"id\":\"9c5ff104eb9c8b10\",\"type\":\"OpcUa-Client\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"endpoint\":\"53f4394dbf12c6b7\",\"action\":\"write\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"Write MyLevel\",\"x\":720,\"y\":1580,\"wires\":[[],",[1107,24861],{},"]},{\"id\":\"77bcb828bec95336\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Call OPC UA Item\",\"info\":\"\",\"x\":470,\"y\":1480,\"wires\":",[1107,24864],{},"},{\"id\":\"afa83dbb46449d4a\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Write OPC UA Item to Client\",\"info\":\"\",\"x\":740,\"y\":1480,\"wires\":",[1107,24867],{},"},{\"id\":\"fa08f0ed04296363\",\"type\":\"ui-switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"\",\"label\":\"Toggle MySwitch\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"topic\":\"topic\",\"topicType\":\"msg\",\"style\":\"\",\"className\":\"\",\"onvalue\":\"true\",\"onvalueType\":\"bool\",\"onicon\":\"\",\"oncolor\":\"\",\"offvalue\":\"false\",\"offvalueType\":\"bool\",\"officon\":\"\",\"offcolor\":\"\",\"x\":210,\"y\":1520,\"wires\":[[\"a66583d91b581cd8\"]]},{\"id\":\"9f591797b56c565d\",\"type\":\"ui-slider\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"group\":\"ec0ecb26fde8db3e\",\"name\":\"\",\"label\":\"Modify MyLevel\",\"tooltip\":\"\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"outs\":\"all\",\"topic\":\"topic\",\"topicType\":\"msg\",\"thumbLabel\":true,\"min\":\"0\",\"max\":\"100\",\"step\":1,\"className\":\"\",\"x\":200,\"y\":1580,\"wires\":[[\"fb7f57b4da5883ae\"]]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp:\u002F\u002F192.168.56.1:53530\u002FOPCUA\u002FSimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"ec0ecb26fde8db3e\",\"type\":\"ui-group\",\"name\":\"MyDevice Status & Control\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"3\",\"height\":\"1\",\"order\":4,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"44d3feb2a1143d7b\",\"type\":\"ui-page\",\"name\":\"OPC UA\",\"ui\":\"5355e0c476f9da3b\",\"path\":\"\u002Fopcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,24870,5343],{},[15,24872,24873,24874,24877,24878,24881,24882,24884,24885,29],{},"We have two values to write, a boolean value corresponding to the node object ",[924,24875,24876],{},"MySwitch",", and an integer value corresponding to the object ",[924,24879,24880],{},"MyLevel",". Therefore, we will use a toggle switch to toggle the ",[924,24883,24876],{},", and a slider to modify ",[924,24886,24880],{},[15,24888,24889],{},"There’s no need to modify the toggle switch properties, other than giving it a name.  The slider needs to have the range modified to match the range of the level, which is 0-100%.",[15,24891,24892,24896,24897,24900,24901,24903],{},[165,24893],{"alt":24894,"dataZoomable":103,"src":24895},"level-range.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Flevel-range.png","\nFor the ",[924,24898,24899],{},"OpcUa-Item"," nodes, copy the ",[924,24902,24421],{}," corresponding to each device,",[15,24905,24906,24910,24911,24913],{},[165,24907],{"alt":24908,"dataZoomable":103,"src":24909},"copy-node-id.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fcopy-node-id.png","\nand paste it into ",[924,24912,24899],{}," node.  You must also ensure the data-type matches with the value you’re writing to.",[15,24915,24916,24920,24921,24924,24925,29],{},[165,24917],{"alt":24918,"dataZoomable":103,"src":24919},"opcua-item.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopcua-item.png","\nThe ",[924,24922,24923],{},"Opc-Ua-Client"," needs to have an endpoint and the action changed to ",[924,24926,24927],{},"WRITE",[15,24929,24930,24934,24935,1413,24937,24939,24940,24942,24943,1318],{},[165,24931],{"alt":24932,"dataZoomable":103,"src":24933},"client-node.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fclient-node.png","\nThe process is the same for ",[924,24936,24876],{},[924,24938,24880],{},", the only difference being what ",[924,24941,24421],{}," is referenced in the  ",[924,24944,24899],{},[15,24946,24947],{},"When deployed, you can confirm values are being written to from the client to the server from the dashboard.",[15,24949,24950],{},[165,24951],{"alt":24952,"dataZoomable":103,"src":24953},"opc-write.gif","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-write.gif",[31,24955,24957],{"id":24956},"read-alarms-events-from-opc-ua-server-using-opcua-event-and-opc-ua-client-nodes","Read Alarms & Events from OPC UA Server Using OpcUa-Event and Opc-Ua-Client Nodes",[15,24959,24960],{},"Our last flow we’ll show how to read OPC UA Alarms & Events.",[15,24962,24963,24844],{},[165,24964],{"alt":24965,"dataZoomable":103,"src":24966},"opc-event-flow.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-event-flow.png",[15,24968,4917,24969,1150],{},[1107,24970,24971,24972,24975,24976,24978,24979,24981,24982,24984,24985,24988,24989,24992,24993,24996,24997,24999,25000,25002,25003,25005,25006,25008,25009,25011,25012,25014,25015,25017,25018,25020,25021,25023,25024,25026],{},"{\"id\":\"a6e9abacd0bdf3b6\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read Alarms & Events From OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,24973,24974],{},"\"90fb4ca64a642edf\",\"b76f64786bc681c3\",\"71e24b671bc03fb8\",\"c7438df35b506470\",\"c7e8919b636cb51d\",\"5952b86dae22b056\",\"04992b24a3836f19\",\"325068cb935cd6d1\",\"5b4d1bd8b342fc05\",\"ba1ea89438335cb8\",\"d662d662c5ccb9c1\",\"1e3956200997581f\",\"0b8ac86e5e4f9f8d\",\"62b2e14ce0429eef\"",",\"x\":94,\"y\":1679,\"w\":1352,\"h\":282},{\"id\":\"90fb4ca64a642edf\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Call OPC UA Item\",\"info\":\"\",\"x\":470,\"y\":1820,\"wires\":",[1107,24977],{},"},{\"id\":\"b76f64786bc681c3\",\"type\":\"OpcUa-Event\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"root\":\"ns=6;s=MyLevel.Alarm\",\"activatecustomevent\":false,\"eventtype\":\"i=2041\",\"customeventtype\":\"\",\"name\":\"MyLevel Alarms\",\"x\":500,\"y\":1860,\"wires\":[[\"c7438df35b506470\"]]},{\"id\":\"71e24b671bc03fb8\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Trigger Alarm Event Capture\",\"props\":",[1107,24980,1132],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":260,\"y\":1860,\"wires\":[[\"b76f64786bc681c3\"]]},{\"id\":\"c7438df35b506470\",\"type\":\"OpcUa-Client\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"endpoint\":\"53f4394dbf12c6b7\",\"action\":\"events\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"securitymode\":\"None\",\"securitypolicy\":\"None\",\"useTransport\":false,\"maxChunkCount\":1,\"maxMessageSize\":8192,\"receiveBufferSize\":8192,\"sendBufferSize\":8192,\"name\":\"Get MyLevel Events\",\"x\":720,\"y\":1860,\"wires\":[[\"c7e8919b636cb51d\",\"5952b86dae22b056\",\"04992b24a3836f19\"],",[1107,24983],{},"]},{\"id\":\"c7e8919b636cb51d\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Event Text\",\"rules\":",[1107,24986,24987],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.Message.text\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":990,\"y\":1800,\"wires\":[[\"d662d662c5ccb9c1\",\"1e3956200997581f\"]]},{\"id\":\"5952b86dae22b056\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Event Time\",\"rules\":",[1107,24990,24991],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.Time\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":990,\"y\":1860,\"wires\":[[\"0b8ac86e5e4f9f8d\"]]},{\"id\":\"04992b24a3836f19\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Event Severity\",\"rules\":",[1107,24994,24995],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.Severity\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1000,\"y\":1920,\"wires\":[[\"62b2e14ce0429eef\"]]},{\"id\":\"325068cb935cd6d1\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Parse Event Dataset\",\"info\":\"\",\"x\":990,\"y\":1760,\"wires\":",[1107,24998],{},"},{\"id\":\"5b4d1bd8b342fc05\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Get OPC Events from Client\",\"info\":\"\",\"x\":720,\"y\":1820,\"wires\":",[1107,25001],{},"},{\"id\":\"ba1ea89438335cb8\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Display Events on Dashboard\",\"info\":\"\",\"x\":1240,\"y\":1720,\"wires\":",[1107,25004],{},"},{\"id\":\"d662d662c5ccb9c1\",\"type\":\"ui-notification\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"ui\":\"5355e0c476f9da3b\",\"position\":\"center center\",\"colorDefault\":true,\"color\":\"#000000\",\"displayTime\":\"3\",\"showCountdown\":true,\"outputs\":1,\"allowDismiss\":true,\"dismissText\":\"Close\",\"raw\":false,\"className\":\"\",\"name\":\"Event Notification\",\"x\":1230,\"y\":1800,\"wires\":[[]]},{\"id\":\"1e3956200997581f\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event\",\"format\":\"",[2526,25007],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1240,\"y\":1840,\"wires\":",[1107,25010],{},"},{\"id\":\"0b8ac86e5e4f9f8d\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Timestamp\",\"format\":\"",[2526,25013],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1280,\"y\":1880,\"wires\":",[1107,25016],{},"},{\"id\":\"62b2e14ce0429eef\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Severity\",\"format\":\"",[2526,25019],{"value":1227},"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1270,\"y\":1920,\"wires\":",[1107,25022],{},"},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp:\u002F\u002F192.168.56.1:53530\u002FOPCUA\u002FSimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,25025,5343],{},",\"showPathInSidebar\":false,\"navigationStyle\":\"default\"},{\"id\":\"ec0ecb26fde8db3e\",\"type\":\"ui-group\",\"name\":\"MyDevice Status & Control\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"3\",\"height\":\"1\",\"order\":4,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"44d3feb2a1143d7b\",\"type\":\"ui-page\",\"name\":\"OPC UA\",\"ui\":\"5355e0c476f9da3b\",\"path\":\"\u002Fopcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"61eee6fc60281b9b\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#0094ce\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}}",[15,25028,25029,25030,25033,25034,25036,25037,25040],{},"We use an inject node to trigger the ",[924,25031,25032],{},"OpcUa-Event"," node.  In the properties of the event node, we get the ",[924,25035,24421],{}," from the ",[924,25038,25039],{},"MyLevelAlarm"," event from the OPC Server -",[15,25042,25043,25047,25048,25050,25051,25053,25054,29],{},[165,25044],{"alt":25045,"dataZoomable":103,"src":25046},"mylevel-event.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fmylevel-event.png","\nAnd copy that ",[924,25049,24421],{}," into the ",[924,25052,25032],{}," node.  Event type will be ",[924,25055,25056],{},"BaseEvent (all)",[15,25058,25059,25063,25064,25066,25067,1242,25069,29],{},[165,25060],{"alt":25061,"dataZoomable":103,"src":25062},"event-node-properties.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fevent-node-properties.png","\nIn the ",[924,25065,24923],{}," node, we set the ",[924,25068,8610],{},[924,25070,25071],{},"EVENTS",[15,25073,25074,25078],{},[165,25075],{"alt":25076,"dataZoomable":103,"src":25077},"client-events.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fclient-events.png","\nIf we stick a debug node on the output of the client event, we can see how the OPC Server annunciates events.",[15,25080,25081,25085,25086,25088,25089,25092],{},[165,25082],{"alt":25083,"dataZoomable":103,"src":25084},"event-debug.png","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fevent-debug.png","\nEvery time ",[924,25087,24880],{}," exceeds certain thresholds (10%, 30%, 70% and 90%) it will flag a ",[924,25090,25091],{},"Level Exceeded"," alarm.   The event is timestamped and assigned a severity level, which we will record and put onto the dashboard.",[15,25094,25095],{},"To make things simple, we’ll only track the last event.  But in a production system, you’d likely want to store these events in a relational database (historian) to keep an alarm history.  We’ll also include a notification pop-up when an alarm occurs to notify someone monitoring the dashboard a new alarm has occurred.",[15,25097,25098],{},"Adding alarms and events to our dashboard creates the following result -",[15,25100,25101],{},[165,25102],{"alt":25103,"dataZoomable":103,"src":25104},"opc-event.gif","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-event.gif",[31,25106,25108],{"id":25107},"using-flowfuse-to-enhance-your-node-red-application-security-scalability-and-robustness","Using FlowFuse to Enhance Your Node-RED Application: Security, Scalability, and Robustness",[15,25110,25111],{},"So, you've successfully built your Node-RED application, congratulations! But now, how do you ensure its security, scalability, and ease of collaboration? What if you want to invite your team to work on the application simultaneously or access it remotely?",[15,25113,25114,25115,25117],{},"Enter ",[63,25116,803],{"href":810},", a cloud-based platform designed to add production-grade features to your Node-RED applications. With FlowFuse, you can seamlessly integrate advanced security measures, scale your application as needed, and collaborate effortlessly with your team. It simplifies management and deployment, turning your Node-RED project into a robust, scalable solution.",[15,25119,25120,25121,25125,25126,25130],{},"If you're interested in learning how to use Node-RED for professional use cases, check out our eBook: ",[63,25122,25124],{"href":25123},"\u002Febooks\u002Fbeginner-guide-to-a-professional-nodered\u002F","Ultimate Beginner's Guide to Professionals",". For additional resources, visit our ",[63,25127,25129],{"href":25128},"\u002Fnode-red\u002Fcore-nodes\u002F","Node-RED Learning Resources section",", where you can explore integrations with different protocols, messaging services, databases, hardware, and much more.",[31,25132,1425],{"id":1424},[15,25134,25135],{},"In this final article, we went over building a OPC UA Client dashboard that can browse the address space, read values from an OPC Server, write values to an OPC Server, and get events from an OPC Server.",[15,25137,25138],{},"This flow provides examples that can serve as a foundation for an interactive OPC Client application built in Node-RED.  This now concludes the OPC UA Series.",[15,25140,25141],{},"full source code for this project -",[15,25143,4917,25144,1150],{},[1107,25145,24186,25146,24190,25148,24193,25150,24196,25152,24206,25158,24209,25160,24218,25166,24227,25172,24230,25174,24234,25176,24242,25182,24251,25188,24259,25194,24263,25196,24266,25198,24269,25200,24272,25202,24283,25208,24292,25214,24302,25220,24308,25224,24313,25228,24318,25232,24574,25234,24578,25236,24581,25238,24585,25240,24588,25242,24595,25246,24599,25248,24604,25252,24609,25256,24614,25260,24617,25262,24620,25264,24623,25266,24627,25268,24630,25270,24633,25272,24636,25274,24640,25276,24643,25278,24647,25280,24650,25282,24654,25284,24657,25286,24664,25290,24667,25292,24670,25294,24674,25296,24679,25300,24682,25302,24685,25304,24688,25306,24691,25308,24694,25310,24697,25312,24700,25314,25316,25317,25320,25321,25323,25324,25327,25328,25330,25331,25335,25336,25338,25339,25343,25344,25346,25347,25349,25350,25352,25353,25356,25357,25359,25360,25363,25364,25366,25367,24853,25369,24856,25371,24859,25373,24862,25375,24865,25377,25379,25380,24975,25382,24978,25384,24981,25386,24984,25388,24988,25390,24992,25392,24996,25394,24999,25396,25002,25398,25005,25400,25008,25402,25011,25404,25014,25406,25017,25408,25020,25410,4957],{},[1107,25147,24189],{},[1107,25149],{},[1107,25151,1132],{},[1107,25153,24199,25154,24202,25156,24205],{},[1107,25155,4948],{},[1107,25157,4948],{},[1107,25159],{},[1107,25161,24212,25162,24202,25164,24205],{},[1107,25163,24215],{},[1107,25165,24215],{},[1107,25167,24221,25168,24202,25170,24205],{},[1107,25169,24224],{},[1107,25171,24224],{},[1107,25173],{},[1107,25175,24233],{},[1107,25177,24237,25178,24202,25180,24205],{},[1107,25179,4944],{},[1107,25181,4944],{},[1107,25183,24245,25184,24202,25186,24205],{},[1107,25185,24248],{},[1107,25187,24248],{},[1107,25189,24254,25190,24202,25192,24205],{},[1107,25191,1855],{},[1107,25193,1855],{},[1107,25195,24262],{},[1107,25197],{},[1107,25199],{},[1107,25201],{},[17139,25203,24275,25204,24279,25206,24282],{},[15,25205,24278],{},[2526,25207],{"value":1227},[17139,25209,24275,25210,24289,25212,24282],{},[15,25211,24288],{},[2526,25213],{"value":1227},[17139,25215,24295,25216,24299,25218,24282],{},[15,25217,24298],{},[2526,25219],{"value":1227},[17139,25221,24305,25222,24282],{},[2526,25223],{"value":1227},[17139,25225,24305,25226,24282],{},[2526,25227],{"value":1227},[17139,25229,24305,25230,24282],{},[2526,25231],{"value":1227},[1107,25233,5343],{},[1107,25235,24577],{},[1107,25237],{},[1107,25239,24584],{},[1107,25241],{},[1107,25243,24591,25244,24594],{},[1107,25245,4944],{},[1107,25247,24598],{},[1107,25249,24591,25250,24594],{},[1107,25251,4948],{},[1107,25253,24591,25254,24594],{},[1107,25255,24224],{},[1107,25257,24591,25258,24594],{},[1107,25259,24215],{},[1107,25261],{},[1107,25263],{},[1107,25265],{},[1107,25267,24626],{},[1107,25269],{},[2526,25271],{"value":1227},[1107,25273],{},[1107,25275,24639],{},[1107,25277,24639],{},[1107,25279,24646],{},[1107,25281],{},[1107,25283,24653],{},[1107,25285],{},[1107,25287,24660,25288,24663],{},[1107,25289,1855],{},[1107,25291,24598],{},[1107,25293],{},[1107,25295,24673],{},[1107,25297,24591,25298,24594],{},[1107,25299,1855],{},[1107,25301,24598],{},[1107,25303],{},[1107,25305],{},[1107,25307],{},[2526,25309],{"value":1227},[1107,25311],{},[2526,25313],{"value":1227},[1107,25315],{},"},{\"id\":\"3d4f386e812e8b5f\",\"type\":\"ui-group\",\"name\":\"StaticData Values\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"4\",\"height\":\"1\",\"order\":3,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"25f95391088d4a08\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read MyDevice Values & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,25318,25319],{},"\"bfe4274963a84e2e\",\"0662c62c7f0cfac0\",\"249c223139c5779e\",\"efb293f13af17dc1\",\"507d2c11c7586957\",\"3b7695a52a1bf6e0\",\"594ec38acadc9673\",\"d4e2915ba92db8a6\",\"85619f0eab615ac7\",\"cd4941a8db3edcb6\",\"ad91d2ca81697fc2\"",",\"x\":94,\"y\":1179,\"w\":1292,\"h\":182},{\"id\":\"bfe4274963a84e2e\",\"type\":\"OpcUa-Browser\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"endpoint\":\"53f4394dbf12c6b7\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"\",\"items\":",[1107,25322],{},",\"name\":\"OPC Client Namespace Browse\",\"x\":590,\"y\":1280,\"wires\":[[\"507d2c11c7586957\"]]},{\"id\":\"0662c62c7f0cfac0\",\"type\":\"inject\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Read MyDevice Values @ 1 second\",\"props\":",[1107,25325,25326],{},"{\"p\":\"payload\"},{\"p\":\"topic\",\"v\":\"Objects.MyObjects.MyDevice.nodeId\",\"vt\":\"flow\"}",",\"repeat\":\"1\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":290,\"y\":1280,\"wires\":[[\"bfe4274963a84e2e\"]]},{\"id\":\"249c223139c5779e\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Read MyDevice\",\"info\":\"\",\"x\":480,\"y\":1240,\"wires\":",[1107,25329],{},"},{\"id\":\"efb293f13af17dc1\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Get MyLevel Value\",\"rules\":",[1107,25332,24591,25333,24594],{},[1107,25334,1855],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1030,\"y\":1260,\"wires\":[[\"cd4941a8db3edcb6\"]]},{\"id\":\"507d2c11c7586957\",\"type\":\"switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"empty check\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":",[1107,25337,24598],{},",\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":810,\"y\":1280,\"wires\":[[\"efb293f13af17dc1\",\"3b7695a52a1bf6e0\"]]},{\"id\":\"3b7695a52a1bf6e0\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Get MySwitch Value\",\"rules\":",[1107,25340,24591,25341,24594],{},[1107,25342,24215],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1040,\"y\":1320,\"wires\":[[\"ad91d2ca81697fc2\"]]},{\"id\":\"594ec38acadc9673\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1260,\"y\":1220,\"wires\":",[1107,25345],{},"},{\"id\":\"d4e2915ba92db8a6\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Parse MyDevice Values\",\"info\":\"\",\"x\":1040,\"y\":1220,\"wires\":",[1107,25348],{},"},{\"id\":\"85619f0eab615ac7\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Discard Empty Datasets\",\"info\":\"\",\"x\":800,\"y\":1240,\"wires\":",[1107,25351],{},"},{\"id\":\"cd4941a8db3edcb6\",\"type\":\"ui-gauge\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Level\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":3,\"height\":3,\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"Level\",\"units\":\"%\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":",[1107,25354,25355],{},"{\"from\":\"0\",\"color\":\"#0094ce\"},{\"from\":\"25\",\"color\":\"#0094ce\"},{\"from\":\"50\",\"color\":\"#0094ce\"},{\"from\":\"100\",\"color\":\"#0094ce\"}",",\"min\":0,\"max\":\"100\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":1250,\"y\":1260,\"wires\":",[1107,25358],{},"},{\"id\":\"ad91d2ca81697fc2\",\"type\":\"ui-led\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"\",\"group\":\"ec0ecb26fde8db3e\",\"order\":-1,\"width\":0,\"height\":0,\"label\":\"Switch\",\"labelPlacement\":\"left\",\"labelAlignment\":\"flex-start\",\"states\":",[1107,25361,25362],{},"{\"value\":\"false\",\"valueType\":\"bool\",\"color\":\"#ff0000\"},{\"value\":\"true\",\"valueType\":\"bool\",\"color\":\"#00ff00\"}",",\"allowColorForValueInMessage\":false,\"shape\":\"circle\",\"showBorder\":true,\"showGlow\":true,\"x\":1250,\"y\":1320,\"wires\":",[1107,25365],{},"},{\"id\":\"ec0ecb26fde8db3e\",\"type\":\"ui-group\",\"name\":\"MyDevice Status & Control\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"3\",\"height\":\"1\",\"order\":4,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"3de6c861611c3afa\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Write Mydevices values to OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,25368,24852],{},[1107,25370],{},[1107,25372],{},[1107,25374],{},[1107,25376],{},[1107,25378],{},"},{\"id\":\"fa08f0ed04296363\",\"type\":\"ui-switch\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"\",\"label\":\"Toggle MySwitch\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"topic\":\"topic\",\"topicType\":\"msg\",\"style\":\"\",\"className\":\"\",\"onvalue\":\"true\",\"onvalueType\":\"bool\",\"onicon\":\"\",\"oncolor\":\"\",\"offvalue\":\"false\",\"offvalueType\":\"bool\",\"officon\":\"\",\"offcolor\":\"\",\"x\":210,\"y\":1520,\"wires\":[[\"a66583d91b581cd8\"]]},{\"id\":\"9f591797b56c565d\",\"type\":\"ui-slider\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"group\":\"ec0ecb26fde8db3e\",\"name\":\"\",\"label\":\"Modify MyLevel\",\"tooltip\":\"\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"outs\":\"all\",\"topic\":\"topic\",\"topicType\":\"msg\",\"thumbLabel\":true,\"min\":\"0\",\"max\":\"100\",\"step\":1,\"className\":\"\",\"x\":200,\"y\":1580,\"wires\":[[\"fb7f57b4da5883ae\"]]},{\"id\":\"a6e9abacd0bdf3b6\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read Alarms & Events From OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":",[1107,25381,24974],{},[1107,25383],{},[1107,25385,1132],{},[1107,25387],{},[1107,25389,24987],{},[1107,25391,24991],{},[1107,25393,24995],{},[1107,25395],{},[1107,25397],{},[1107,25399],{},[2526,25401],{"value":1227},[1107,25403],{},[2526,25405],{"value":1227},[1107,25407],{},[2526,25409],{"value":1227},[1107,25411],{},{"title":103,"searchDepth":104,"depth":104,"links":25413},[25414,25415,25416,25417,25418,25419,25420,25421,25422],{"id":24052,"depth":107,"text":24053},{"id":24092,"depth":107,"text":24093},{"id":24144,"depth":107,"text":24145},{"id":24168,"depth":107,"text":24169},{"id":24470,"depth":107,"text":24471},{"id":24833,"depth":107,"text":24834},{"id":24956,"depth":107,"text":24957},{"id":25107,"depth":107,"text":25108},{"id":1424,"depth":107,"text":1425},{"type":748,"title":25424,"description":25425},"Deploy Your OPC UA Dashboard with FlowFuse","Sign up for FlowFuse to securely deploy, share, and scale your Node-RED OPC UA client dashboard applications with team collaboration and DevOps pipelines.","2023-07-27","Building a Dashboard-Driven OPC UA Client to Browse, Read, Write, and Get Events from a 3rd party OPC UA Server","blog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-3\u002Fopc-ua-3-title-image.png",{"keywords":25430,"excerpt":25431},"opc ua client node-red, opc ua dashboard, node-red opcua client, opc ua browser node-red, read write opc ua node-red, opc ua alarms events, prosys simulation server",{"type":12,"value":25432},[25433],[15,25434,24030,25435,24034,25437,24038],{},[63,25436,24033],{"href":11759},[63,25438,24037],{"href":2640},"\u002Fblog\u002F2023\u002F07\u002Fhow-to-build-a-opc-client-dashboard-in-node-red",{"title":24023,"description":25427},{"loc":25439},"blog\u002F2023\u002F07\u002Fhow-to-build-a-opc-client-dashboard-in-node-red","Interactive OPC UA Client dashboard that communicates with a 3rd party OPC UA Server",[8650,10,138,11799,7457],"This part of the OPC UA series shows how to build an interactive Node-RED dashboard that connects to a third-party OPC UA server to browse the address space, read and write values, and display alarms and events. Complete flow source code is provided, making it a practical starting point for production OPC UA client applications.","iTvLAoss4hTIIwo4x9-95lGhpygpJvE5KrP3OBAPuPY",{"id":25448,"title":25449,"authors":25450,"body":25451,"cta":26313,"date":26316,"description":26317,"extension":116,"image":26318,"lastUpdated":6203,"meta":26319,"navigation":129,"path":26325,"seo":26326,"sitemap":26327,"stem":26328,"subtitle":26329,"tags":26330,"tldr":26331,"video":3,"__hash__":26332},"blog\u002Fblog\u002F2023\u002F07\u002Fimages-in-node-red-dashboards.md","How to add images to Node-RED dashboards when using FlowFuse (2026)",[19494],{"type":12,"value":25452,"toc":26290},[25453,25456,25459,25470,25472,25475,25502,25506,25514,25518,25523,25534,25538,25550,25554,25565,25569,25578,25582,25602,25606,25638,25642,25839,25843,25874,25911,25914,25918,25921,25924,25928,25931,25935,25938,25949,25952,25955,25959,25962,26003,26007,26015,26019,26022,26106,26109,26112,26118,26120,26263,26266,26272,26275,26279,26282,26284,26287],[15,25454,25455],{},"Using images in your Node-RED dashboards can significantly improve your users' experience. The most common method to add images to dashboards is to store them within the filesystem of an Node-RED instance but sometimes that's not an option. How can you easily use images when working in a containerized environment such as Docker, or Kubernetes? We will also explore latest feature from FlowFuse that makes this step super easy.",[15,25457,25458],{},"When designing a dashboard, images allow you to significantly enrich your content. Some examples include:",[39,25460,25461,25464,25467],{},[42,25462,25463],{},"displaying maps to guide engineers to a problem which needs resolving.",[42,25465,25466],{},"displaying pictures of specific hardware on a factory-floor which needs to be checked.",[42,25468,25469],{},"displaying physical tools which should be used to resolve a problem.",[399,25471,820],{"id":819},[15,25473,25474],{},"Before we begin, ensure you have the following custom nodes installed:",[39,25476,25477,25486,25494],{},[42,25478,25479,25482,25483,29],{},[63,25480,1536],{"href":24067,"rel":25481},[67]," - A set of dashboard nodes for Node-RED. We will use this dashboard to demonstrate how to quickly display images using static assets. If you're a beginner and want to dive deeper, refer to ",[63,25484,25485],{"href":3671},"Getting started with FlowFuse Dashboarad",[42,25487,25488,25493],{},[63,25489,25492],{"href":25490,"rel":25491},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-string",[67],"node-red-contrib-string"," - A string manipulation node based on the lightweight stringjs library.",[42,25495,25496,25501],{},[63,25497,25500],{"href":25498,"rel":25499},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-node-base64",[67],"node-red-node-base64"," - A Node-RED node to encode and decode data to and from base64.",[31,25503,25505],{"id":25504},"easily-add-images-to-node-red-dashboards-with-flowfuses-static-asset-service","Easily Add Images to Node-RED Dashboards with FlowFuse’s Static Asset Service",[15,25507,25508,25513],{},[63,25509,25512],{"href":25510,"rel":25511},"https:\u002F\u002Fflowforge.com\u002Fdocs\u002Fuser\u002Fstatic-asset-service\u002F",[67],"FlowFuse's static assets"," service provides a simple way to manage images and other assets in Node-RED. Follow these steps to quickly add images to your Node-RED dashboard.",[399,25515,25517],{"id":25516},"steps-to-add-images-using-the-static-asset-service","Steps to Add Images Using the Static Asset Service:",[25519,25520,25522],"h5",{"id":25521},"_1-access-the-static-assets-service","1. Access the Static Assets Service",[39,25524,25525],{},[42,25526,25527,25528,25530,25531,9430],{},"Log into your FlowFuse account, navigate to your ",[19,25529,831],{},", and click on the ",[19,25532,25533],{},"Static Assets Service",[25519,25535,25537],{"id":25536},"_2-create-a-new-folder-optional","2. Create a New Folder (Optional)",[39,25539,25540],{},[42,25541,278,25542,25545,25546,25549],{},[19,25543,25544],{},"New Folder"," button to create a folder that will help you organize your assets. Provide a folder name and ",[19,25547,25548],{},"confirm"," the creation.",[25519,25551,25553],{"id":25552},"_3-upload-your-image","3. Upload Your Image",[39,25555,25556],{},[42,25557,25558,25559,25562,25563,29],{},"Enter the folder (or skip this step if not using folders), click ",[19,25560,25561],{},"Upload",", select your image file, and ",[19,25564,25548],{},[25519,25566,25568],{"id":25567},"_4-copy-the-image-path","4. Copy the Image Path",[39,25570,25571],{},[42,25572,25573,25574,25577],{},"Once uploaded, click the ",[19,25575,25576],{},"copy icon"," next to the image to get the path for use in Node-RED.",[25519,25579,25581],{"id":25580},"_5-set-up-the-image-flow-in-node-red","5. Set Up the Image Flow in Node-RED",[39,25583,25584,25587,25592],{},[42,25585,25586],{},"Open the Node-RED editor for the relevant instance.",[42,25588,1045,25589,25591],{},[924,25590,1588],{}," node onto the canvas and set it to trigger immediately when the flow is deployed.",[42,25593,1263,25594,25597,25598,25601],{},[924,25595,25596],{},"read file"," node, paste the copied file path in the ",[19,25599,25600],{},"Filename"," field, and set the output to Single buffer object.",[25519,25603,25605],{"id":25604},"_6-prepare-the-image-for-display","6. Prepare the Image for Display",[39,25607,25608,25627],{},[42,25609,1321,25610,25613,25614,25617,25618,25621,25622,1242,25624,29],{},[924,25611,25612],{},"string"," node to convert the buffer to a base64 string. Set ",[19,25615,25616],{},"From"," as ",[924,25619,25620],{},"msg.filename"," and adjust the ",[19,25623,1650],{},[924,25625,25626],{},"getmost",[42,25628,1321,25629,25631,25632,25634],{},[924,25630,9518],{}," node and configure it to add the elements shown in the following image:",[5368,25633],{},[165,25635],{"alt":25636,"src":25637,"title":25636},"The change node showing added elements","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fchange-node.png",[25519,25639,25641],{"id":25640},"_7-display-the-image-in-the-dashboard","7. Display the Image in the Dashboard",[39,25643,25644,25648],{},[42,25645,1263,25646,1216],{},[924,25647,13863],{},[42,25649,25650,25651,25653,25654,25657,25658,25660,25661,25663,25664],{},"Add the code in ",[924,25652,13863],{}," with an ",[924,25655,25656],{},"\u003Cimg>"," tag, configuring the ",[924,25659,23249],{}," attribute with ",[924,25662,1227],{},", as shown in the following code. Alternatively, you can use the following code directly if you want to display the image in the top-left corner of your dashboard header:",[1756,25665,25667],{"className":1758,"code":25666,"language":1760,"meta":103,"style":103},"\u003Ctemplate>\n    \u003CTeleport v-if=\"mounted\" to=\"#app-bar-title\">\n        \u003Cimg :src=\"msg.payload\" style=\"height: 32px;\" \u002F>\n    \u003C\u002FTeleport>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n    export default {\n        data() {\n            return {\n                mounted: false\n            }\n        },\n        mounted() {\n            this.mounted = true;\n        }\n    }\n\u003C\u002Fscript>\n",[924,25668,25669,25677,25709,25732,25740,25748,25752,25760,25767,25776,25783,25793,25798,25802,25811,25823,25827,25831],{"__ignoreMap":103},[1107,25670,25671,25673,25675],{"class":1765,"line":1766},[1107,25672,17136],{"class":1783},[1107,25674,17139],{"class":1946},[1107,25676,17142],{"class":1783},[1107,25678,25679,25681,25684,25686,25688,25690,25693,25695,25698,25700,25702,25705,25707],{"class":1765,"line":107},[1107,25680,19861],{"class":1783},[1107,25682,25683],{"class":10543},"Teleport",[1107,25685,17364],{"class":1775},[1107,25687,1784],{"class":1783},[1107,25689,1970],{"class":1783},[1107,25691,25692],{"class":1966},"mounted",[1107,25694,1970],{"class":1783},[1107,25696,25697],{"class":1775}," to",[1107,25699,1784],{"class":1783},[1107,25701,1970],{"class":1783},[1107,25703,25704],{"class":1966},"#app-bar-title",[1107,25706,1970],{"class":1783},[1107,25708,17142],{"class":1783},[1107,25710,25711,25714,25716,25719,25721,25723,25725,25728,25730],{"class":1765,"line":294},[1107,25712,25713],{"class":1783},"        \u003C",[1107,25715,165],{"class":1946},[1107,25717,25718],{"class":1783}," :src=\"msg.payload\" ",[1107,25720,2582],{"class":1775},[1107,25722,1784],{"class":1783},[1107,25724,1970],{"class":1783},[1107,25726,25727],{"class":1966},"height: 32px;",[1107,25729,1970],{"class":1783},[1107,25731,17501],{"class":1783},[1107,25733,25734,25736,25738],{"class":1765,"line":104},[1107,25735,19931],{"class":1783},[1107,25737,25683],{"class":10543},[1107,25739,17142],{"class":1783},[1107,25741,25742,25744,25746],{"class":1765,"line":1868},[1107,25743,17307],{"class":1783},[1107,25745,17139],{"class":1946},[1107,25747,17142],{"class":1783},[1107,25749,25750],{"class":1765,"line":1896},[1107,25751,1921],{"emptyLinePlaceholder":129},[1107,25753,25754,25756,25758],{"class":1765,"line":1623},[1107,25755,17136],{"class":1783},[1107,25757,94],{"class":1946},[1107,25759,17142],{"class":1783},[1107,25761,25762,25765],{"class":1765,"line":1924},[1107,25763,25764],{"class":1779},"    export default ",[1107,25766,9062],{"class":1783},[1107,25768,25769,25772,25774],{"class":1765,"line":1930},[1107,25770,25771],{"class":15356},"        data",[1107,25773,20954],{"class":1779},[1107,25775,9062],{"class":1783},[1107,25777,25778,25781],{"class":1765,"line":1940},[1107,25779,25780],{"class":1779},"            return ",[1107,25782,9062],{"class":1783},[1107,25784,25785,25788,25790],{"class":1765,"line":1976},[1107,25786,25787],{"class":10543},"                mounted",[1107,25789,992],{"class":1783},[1107,25791,25792],{"class":20533}," false\n",[1107,25794,25795],{"class":1765,"line":2002},[1107,25796,25797],{"class":1783},"            }\n",[1107,25799,25800],{"class":1765,"line":2029},[1107,25801,10578],{"class":1783},[1107,25803,25804,25807,25809],{"class":1765,"line":1691},[1107,25805,25806],{"class":15356},"        mounted",[1107,25808,20954],{"class":1779},[1107,25810,9062],{"class":1783},[1107,25812,25813,25816,25818,25821],{"class":1765,"line":2080},[1107,25814,25815],{"class":1779},"            this.mounted ",[1107,25817,1784],{"class":1783},[1107,25819,25820],{"class":20533}," true",[1107,25822,1805],{"class":1779},[1107,25824,25825],{"class":1765,"line":7873},[1107,25826,10620],{"class":1783},[1107,25828,25829],{"class":1765,"line":7879},[1107,25830,10820],{"class":1783},[1107,25832,25833,25835,25837],{"class":1765,"line":7885},[1107,25834,17307],{"class":1783},[1107,25836,94],{"class":1946},[1107,25838,17142],{"class":1783},[25519,25840,25842],{"id":25841},"_8-connect-the-nodes","8. Connect the Nodes",[39,25844,25845],{},[42,25846,25847,25848,25851,25852,1720,25854,25851,25857,25859,25860,25863,25864,25866,25867,1318,25869,25871],{},"Finally connect the nodes in the following order:\nthe ",[19,25849,25850],{},"output"," of the ",[924,25853,1588],{},[19,25855,25856],{},"input",[924,25858,25596],{}," node, then link to the ",[924,25861,25862],{},"string node",", followed by the ",[924,25865,9518],{}," node, and finally to the ",[924,25868,13863],{},[5368,25870],{},[924,25872,25873],{},"inject → read file → string → change → ui-template",[15,25875,4917,25876,1150],{},[1107,25877,25878,25879,25882,25883,25889,25890,25893,25894,25896,25897,25900,25901,25907,25908,25910],{},"{\"id\":\"e50f7c57189d62f8\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1107,25880,25881],{},"\"3bf4c71150bd0524\",\"4d3ca1b96c181645\",\"315948cc6a2cc9e8\",\"fcf061d4cc732e2e\",\"6563d8715af4cb06\",\"d40caa36040de881\"",",\"x\":514,\"y\":699,\"w\":1752,\"h\":82},{\"id\":\"3bf4c71150bd0524\",\"type\":\"ui-template\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"group\":\"\",\"page\":\"\",\"ui\":\"25f447d87d1ce5c9\",\"name\":\"Display image\",\"order\":0,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,25884,24295,25885,25886,25888],{},"\\n    \u003CTeleport v-if=\"mounted\" to=\"#app-bar-title\">\\n        \u003Cimg ",[23249,25887],{},"=\"msg.payload\" style=\"height: 32px;\" \u002F>\\n    \\n","\\n\\n",[94,25891,25892],{},"\\n    export default {\\n        data() {\\n            return {\\n                mounted: false\\n            }\\n        },\\n        mounted() {\\n            \u002F\u002F Set mounted to true when the component is mounted\\n            this.mounted = true\\n        }\\n    }\\n","\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"widget:ui\",\"className\":\"\",\"x\":2160,\"y\":740,\"wires\":[[]]},{\"id\":\"4d3ca1b96c181645\",\"type\":\"inject\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"name\":\"\",\"props\":",[1107,25895,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":630,\"y\":740,\"wires\":[[\"315948cc6a2cc9e8\"]]},{\"id\":\"315948cc6a2cc9e8\",\"type\":\"file in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"name\":\"\",\"filename\":\"Images\u002Fff-logo--wordmark--light.png\",\"filenameType\":\"str\",\"format\":\"\",\"chunk\":false,\"sendError\":false,\"encoding\":\"none\",\"allProps\":false,\"x\":880,\"y\":740,\"wires\":[[\"d40caa36040de881\"]]},{\"id\":\"fcf061d4cc732e2e\",\"type\":\"change\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"name\":\"Add the file type to the mimetype, add to image content\",\"rules\":",[1107,25898,25899],{},"{\"t\":\"set\",\"p\":\"mimetype\",\"pt\":\"msg\",\"to\":\"\"data:image\u002F\"&msg.filetype&\";base64,\"\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"output\",\"pt\":\"msg\",\"to\":\"msg.mimetype&msg.payload\",\"tot\":\"jsonata\"},{\"t\":\"move\",\"p\":\"output\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1820,\"y\":740,\"wires\":[[\"3bf4c71150bd0524\"]]},{\"id\":\"6563d8715af4cb06\",\"type\":\"string\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"name\":\"Get file type from file name\",\"methods\":",[1107,25902,25903,25904,4957],{},"{\"name\":\"getRightMost\",\"params\":",[1107,25905,25906],{},"{\"type\":\"str\",\"value\":\".\"}",",\"prop\":\"filename\",\"propout\":\"filetype\",\"object\":\"msg\",\"objectout\":\"msg\",\"x\":1480,\"y\":740,\"wires\":[[\"fcf061d4cc732e2e\"]]},{\"id\":\"d40caa36040de881\",\"type\":\"base64\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"e50f7c57189d62f8\",\"name\":\"Convert Buffer to Base 64 String\",\"action\":\"\",\"property\":\"payload\",\"x\":1210,\"y\":740,\"wires\":[[\"6563d8715af4cb06\"]]},{\"id\":\"25f447d87d1ce5c9\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,25909,2560],{},",\"showPathInSidebar\":false,\"showPageTitle\":false,\"titleBarStyle\":\"default\"}",[15,25912,25913],{},"Using the FlowFuse Static Assets service is highly beneficial when you want to display images in Node-RED dashboards, as it saves time compared to alternative solutions. However, it’s important to note that moving Node-RED instances through a DevOps pipeline currently does not support handling static assets. This feature is expected in future updates. If you want to manage images effectively within your Node-RED dashboards, consider the alternative solutions discussed in this blog, ensuring that the movement of instances does not affect the usage of these assets.",[31,25915,25917],{"id":25916},"why-not-just-store-them-in-node-reds-host-operating-system","Why not just store them in Node-RED's host operating system?",[15,25919,25920],{},"Storing images locally can work well when you can access and edit the images on an operating system, but that approach doesn't scale if you are moving instances through a DevOps pipeline. It can also not work well when deploying to environments where you don't have easy access to the host operating system.",[15,25922,25923],{},"How can we include images in dashboards, and be confident that a given build of an application will show the correct images, no matter where your Node-RED instances are hosted?",[31,25925,25927],{"id":25926},"inspiration","Inspiration",[15,25929,25930],{},"There are various solutions to this problem, I wanted to share one I came across when working with a FlowFuse customer recently. I've modified the flows to make them more general in design, but the underlying principal is the same. I asked if I was OK to credit the customer but they said there was no need. Thanks for the inspiration, kind customer!",[31,25932,25934],{"id":25933},"solution-explanation","Solution explanation",[15,25936,25937],{},"There are three key sections to this solution:",[271,25939,25940,25943,25946],{},[42,25941,25942],{},"Pull the images we need from URLs",[42,25944,25945],{},"Store those images in the temporary filesystem of Node-RED",[42,25947,25948],{},"Serve up those images as needed in the dashboard",[15,25950,25951],{},"It is possible for us to skip step 2, but I wanted to have the images stored locally, in the Node-RED instance. storing the images locally will improve the loading times of the dashboard. This is especially beneficial when your dashboard is dynamically displaying relevant images, e.g. to show an image of a specific machine which needs to be attended to.",[15,25953,25954],{},"The key benefit of pulling the images from URLs this way is, no matter where you are running Node-RED, the correct images will be shown in your dashboard.",[31,25956,25958],{"id":25957},"prequsite","Prequsite",[15,25960,25961],{},"Before moving forward, ensure you have the following nodes installed, as the flows shared later will require them:",[39,25963,25964,25972,25980,25987,25992,25998],{},[42,25965,25966,25971],{},[63,25967,25970],{"href":25968,"rel":25969},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-calc",[67],"node-red-contrib-calc"," - A Node-RED node to perform basic mathematical calculations.",[42,25973,25974,25979],{},[63,25975,25978],{"href":25976,"rel":25977},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-image-output",[67],"node-red-contrib-image-output"," - A simple way to preview and examine images in your flows.",[42,25981,25982,25986],{},[63,25983,4695],{"href":25984,"rel":25985},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-os",[67]," - Nodes for obtaining system information like CPU usage.",[42,25988,25989,25493],{},[63,25990,25492],{"href":25490,"rel":25991},[67],[42,25993,25994,25997],{},[63,25995,1536],{"href":24067,"rel":25996},[67]," - A set of dashboard nodes for Node-RED.",[42,25999,26000,25501],{},[63,26001,25500],{"href":25498,"rel":26002},[67],[31,26004,26006],{"id":26005},"file-and-file-in-nodes","File and file-in nodes",[15,26008,26009,26010,26014],{},"I've included the flows as json below so you can try them out yourself. Please note, I'm using FlowFuse's own ",[63,26011,26013],{"href":26012},"\u002Fdocs\u002Fuser\u002Ffilenodes\u002F","file and file-in nodes"," in these examples. If you want to use these flows on hosting other than FlowFuse, you will need to replace the nodes with the standard Node-RED file and file-in nodes.",[31,26016,26018],{"id":26017},"the-flows","The flows",[15,26020,26021],{},"The first flow takes image URLs in an array, each image is downloaded, processed, then saved to the local file storage. Let's take a look at the flow:",[15,26023,4917,26024,1150],{},[1107,26025,26026,26027,26030,26031,26034,26035,14493,26037,26044,26045,26048,26049,26051,26052,26055,26056,26058,26059,26062,26063,26066,26067,26069,26070,26073,26074,26077,26078,26083,26084,26087,26088,26091,26092,26094,26095,26098,26099,26101,26102,26105],{},"{\"id\":\"6b8059f703d0f574\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"name\":\"Write the images to disk from the URLs\",\"style\":{\"label\":true},\"nodes\":",[1107,26028,26029],{},"\"04fb6911559797a0\",\"8a3c077f0f85a905\",\"22c5026dd58e418b\",\"6fcca5cfee2bcb89\"",",\"x\":38,\"y\":53,\"w\":1004,\"h\":434},{\"id\":\"04fb6911559797a0\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Inject the image URLs to download\",\"style\":{\"label\":true},\"nodes\":",[1107,26032,26033],{},"\"e635ceb0577a86d5\",\"29fe40a054be5b2b\",\"1e81a35c27aae6ad\"",",\"x\":74,\"y\":79,\"w\":502,\"h\":82},{\"id\":\"e635ceb0577a86d5\",\"type\":\"inject\",\"z\":\"c6f2a894be05d857\",\"g\":\"04fb6911559797a0\",\"name\":\"Send in image URLs as an array\",\"props\":",[1107,26036,5884],{},[1107,26038,1970,26039,1970],{},[63,26040,26043],{"href":26041,"rel":26042},"https:\u002F\u002Fopenjsf.org\u002Fwp-content\u002Fuploads\u002Fsites\u002F84\u002F2023\u002F02\u002Fff-logo-wordmark-light_4x.png%5C%22,%5C%22https:\u002F\u002Fnodered.org\u002Fimages\u002Fnr-image-1.png%5C%22,%5C%22\u002Fimg\u002Fscreen-pseudo-overview-2QvTVle3Mr-384.avif%5C",[67],"https:\u002F\u002Fopenjsf.org\u002Fwp-content\u002Fuploads\u002Fsites\u002F84\u002F2023\u002F02\u002Fff-logo-wordmark-light_4x.png\\\",\\\"https:\u002F\u002Fnodered.org\u002Fimages\u002Fnr-image-1.png\\\",\\\"\u002Fimg\u002Fscreen-pseudo-overview-2QvTVle3Mr-384.avif\\","\",\"payloadType\":\"json\",\"x\":250,\"y\":120,\"wires\":[[\"29fe40a054be5b2b\"]]},{\"id\":\"29fe40a054be5b2b\",\"type\":\"split\",\"z\":\"c6f2a894be05d857\",\"g\":\"04fb6911559797a0\",\"name\":\"\",\"splt\":\"\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":450,\"y\":120,\"wires\":[[\"1e81a35c27aae6ad\"]]},{\"id\":\"1e81a35c27aae6ad\",\"type\":\"link out\",\"z\":\"c6f2a894be05d857\",\"g\":\"04fb6911559797a0\",\"name\":\"link out 3\",\"mode\":\"link\",\"links\":",[1107,26046,26047],{},"\"f582702ec222069c\"",",\"x\":535,\"y\":120,\"wires\":",[1107,26050],{},"},{\"id\":\"8a3c077f0f85a905\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Download the images\",\"style\":{\"label\":true},\"nodes\":",[1107,26053,26054],{},"\"453f3b9d7d312bd2\",\"ecbd7b1a410ecd9d\",\"4d650baa2118036e\",\"f582702ec222069c\"",",\"x\":84,\"y\":179,\"w\":532,\"h\":82},{\"id\":\"453f3b9d7d312bd2\",\"type\":\"http request\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"Get the image\",\"method\":\"GET\",\"ret\":\"bin\",\"paytoqs\":\"ignore\",\"url\":\"\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":",[1107,26057],{},",\"x\":460,\"y\":220,\"wires\":[[\"4d650baa2118036e\"]]},{\"id\":\"ecbd7b1a410ecd9d\",\"type\":\"change\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"Set URL to download\",\"rules\":",[1107,26060,26061],{},"{\"t\":\"move\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"url\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":260,\"y\":220,\"wires\":[[\"453f3b9d7d312bd2\"]]},{\"id\":\"4d650baa2118036e\",\"type\":\"link out\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"link out 1\",\"mode\":\"link\",\"links\":",[1107,26064,26065],{},"\"8bc38803dec97185\"",",\"x\":575,\"y\":220,\"wires\":",[1107,26068],{},"},{\"id\":\"f582702ec222069c\",\"type\":\"link in\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"link in 3\",\"links\":",[1107,26071,26072],{},"\"1e81a35c27aae6ad\"",",\"x\":125,\"y\":220,\"wires\":[[\"ecbd7b1a410ecd9d\"]]},{\"id\":\"22c5026dd58e418b\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Save the images to the local storage\",\"style\":{\"label\":true},\"nodes\":",[1107,26075,26076],{},"\"95c819560d22f394\",\"0fe7f013b6356c5d\",\"7edc6cb2b0d243db\",\"829bfc8e6e293ada\",\"8bc38803dec97185\",\"98980d88013c6e12\"",",\"x\":84,\"y\":279,\"w\":932,\"h\":82},{\"id\":\"95c819560d22f394\",\"type\":\"base64\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"convert to base64\",\"action\":\"str\",\"property\":\"payload\",\"x\":250,\"y\":320,\"wires\":[[\"829bfc8e6e293ada\"]]},{\"id\":\"0fe7f013b6356c5d\",\"type\":\"file\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"Write file to storage\",\"filename\":\"filename\",\"filenameType\":\"msg\",\"appendNewline\":true,\"createDir\":false,\"overwriteFile\":\"true\",\"encoding\":\"none\",\"x\":850,\"y\":320,\"wires\":[[\"98980d88013c6e12\"]]},{\"id\":\"7edc6cb2b0d243db\",\"type\":\"string\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"Get filename from the URL\",\"methods\":",[1107,26079,25903,26080,4957],{},[1107,26081,26082],{},"{\"type\":\"str\",\"value\":\"\u002F\"}",",\"prop\":\"responseUrl\",\"propout\":\"filename\",\"object\":\"msg\",\"objectout\":\"msg\",\"x\":620,\"y\":320,\"wires\":[[\"0fe7f013b6356c5d\"]]},{\"id\":\"829bfc8e6e293ada\",\"type\":\"image\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"preview\",\"width\":\"150\",\"data\":\"payload\",\"dataType\":\"msg\",\"thumbnail\":false,\"active\":true,\"pass\":true,\"outputs\":1,\"x\":420,\"y\":320,\"wires\":[[\"7edc6cb2b0d243db\"]]},{\"id\":\"8bc38803dec97185\",\"type\":\"link in\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"link in 1\",\"links\":",[1107,26085,26086],{},"\"4d650baa2118036e\"",",\"x\":125,\"y\":320,\"wires\":[[\"95c819560d22f394\"]]},{\"id\":\"98980d88013c6e12\",\"type\":\"link out\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"link out 2\",\"mode\":\"link\",\"links\":",[1107,26089,26090],{},"\"1e94b5bab542830a\"",",\"x\":975,\"y\":320,\"wires\":",[1107,26093],{},"},{\"id\":\"6fcca5cfee2bcb89\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Output a debug once all images have been processed\",\"style\":{\"label\":true},\"nodes\":",[1107,26096,26097],{},"\"d1a6feea3ac829c6\",\"119f8008752bc4fb\",\"1e94b5bab542830a\"",",\"x\":64,\"y\":379,\"w\":382,\"h\":82},{\"id\":\"d1a6feea3ac829c6\",\"type\":\"debug\",\"z\":\"c6f2a894be05d857\",\"g\":\"6fcca5cfee2bcb89\",\"name\":\"debug 140\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":330,\"y\":420,\"wires\":",[1107,26100],{},"},{\"id\":\"119f8008752bc4fb\",\"type\":\"join\",\"z\":\"c6f2a894be05d857\",\"g\":\"6fcca5cfee2bcb89\",\"name\":\"\",\"mode\":\"auto\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\n\",\"joinerType\":\"str\",\"accumulate\":\"false\",\"timeout\":\"\",\"count\":\"\",\"reduceRight\":false,\"x\":190,\"y\":420,\"wires\":[[\"d1a6feea3ac829c6\"]]},{\"id\":\"1e94b5bab542830a\",\"type\":\"link in\",\"z\":\"c6f2a894be05d857\",\"g\":\"6fcca5cfee2bcb89\",\"name\":\"link in 2\",\"links\":",[1107,26103,26104],{},"\"98980d88013c6e12\"",",\"x\":105,\"y\":420,\"wires\":[[\"119f8008752bc4fb\"]]}",[15,26107,26108],{},"We've now downloaded the images we need, and saved them to our local storage, to make them load more quickly when a user views them in the dashboard.",[15,26110,26111],{},"Onto the second flow, which will get the images from the local storage and then load them into the dashboard. Let's take a look at it:",[15,26113,26114],{},[165,26115],{"alt":26116,"src":26117,"title":26116},"Get the images from the local storage and place them in a dashboard","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fload-images-from-disk-and-show-in-dashboard.png",[15,26119,24181],{},[15,26121,4917,26122,1150],{},[1107,26123,26124,26125,26128,26129,26132,26133,26136,26137,26140,26141,26143,26144,26147,26148,26150,26151,26155,26156,26159,26160,26163,26164,26166,26167,26170,26171,26174,26175,1955,26178,26181,26182,26185,26186,26189,26190,26192,26193,26204,26205,26213,26214,26222,26223,26226,26227,26229,26230,26233,26234,26237,26238,26240,26241,26244,26245,26247,26248,26251,26252,26254,26255,26262],{},"{\"id\":\"596006fe79275d55\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"name\":\"Get the images from the filestore and display in the Dashboard\",\"style\":{\"label\":true},\"nodes\":",[1107,26126,26127],{},"\"be421f8265c4c090\",\"d2dc98b4f3d6b968\",\"b9029adc6097bd14\",\"1e54acecec6bd411\",\"99aaee673fc70bc2\"",",\"x\":1228,\"y\":3733,\"w\":974,\"h\":654},{\"id\":\"be421f8265c4c090\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Get the images from the local storage\",\"style\":{\"label\":true},\"nodes\":",[1107,26130,26131],{},"\"c698b2945583e126\",\"ab22b978a2f5d80f\",\"2836914f9b643e34\",\"8e32aba67d9ac9d4\"",",\"x\":1264,\"y\":3899,\"w\":492,\"h\":82},{\"id\":\"c698b2945583e126\",\"type\":\"image\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"be421f8265c4c090\",\"name\":\"preview\",\"width\":\"150\",\"data\":\"payload\",\"dataType\":\"msg\",\"thumbnail\":false,\"active\":true,\"pass\":true,\"outputs\":1,\"x\":1620,\"y\":3940,\"wires\":[[\"8e32aba67d9ac9d4\"]]},{\"id\":\"ab22b978a2f5d80f\",\"type\":\"file in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"be421f8265c4c090\",\"name\":\"Read file from storage\",\"filename\":\"payload\",\"filenameType\":\"msg\",\"format\":\"utf8\",\"chunk\":false,\"sendError\":false,\"encoding\":\"none\",\"allProps\":false,\"x\":1440,\"y\":3940,\"wires\":[[\"c698b2945583e126\"]]},{\"id\":\"2836914f9b643e34\",\"type\":\"link in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"be421f8265c4c090\",\"name\":\"link in 4\",\"links\":",[1107,26134,26135],{},"\"a9958e1e4b07191a\"",",\"x\":1305,\"y\":3940,\"wires\":[[\"ab22b978a2f5d80f\"]]},{\"id\":\"8e32aba67d9ac9d4\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"be421f8265c4c090\",\"name\":\"link out 5\",\"mode\":\"link\",\"links\":",[1107,26138,26139],{},"\"bbdd3a2e737e1074\"",",\"x\":1715,\"y\":3940,\"wires\":",[1107,26142],{},"},{\"id\":\"d2dc98b4f3d6b968\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Prepare each image to be shown in the dashboard\",\"style\":{\"label\":true},\"nodes\":",[1107,26145,26146],{},"\"54f0e7fddf447d10\",\"d856077cc6d37093\",\"bbdd3a2e737e1074\",\"2d29c953bcdab787\"",",\"x\":1264,\"y\":3999,\"w\":832,\"h\":82},{\"id\":\"54f0e7fddf447d10\",\"type\":\"change\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"Add the file type to the mimetype, add to image content\",\"rules\":",[1107,26149,25899],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1810,\"y\":4040,\"wires\":[[\"2d29c953bcdab787\"]]},{\"id\":\"d856077cc6d37093\",\"type\":\"string\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"Get file type from file name\",\"methods\":",[1107,26152,25903,26153,4957],{},[1107,26154,25906],{},",\"prop\":\"filename\",\"propout\":\"filetype\",\"object\":\"msg\",\"objectout\":\"msg\",\"x\":1460,\"y\":4040,\"wires\":[[\"54f0e7fddf447d10\"]]},{\"id\":\"bbdd3a2e737e1074\",\"type\":\"link in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"link in 5\",\"links\":",[1107,26157,26158],{},"\"8e32aba67d9ac9d4\"",",\"x\":1305,\"y\":4040,\"wires\":[[\"d856077cc6d37093\"]]},{\"id\":\"2d29c953bcdab787\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"link out 6\",\"mode\":\"link\",\"links\":",[1107,26161,26162],{},"\"c5e4febe48363987\"",",\"x\":2055,\"y\":4040,\"wires\":",[1107,26165],{},"},{\"id\":\"b9029adc6097bd14\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Send the images to the correct section of the dashboard\",\"style\":{\"label\":true},\"nodes\":",[1107,26168,26169],{},"\"fa873d7bccd2b6be\",\"c5e4febe48363987\",\"d80e5a54a7c5b2f8\",\"fb0c76a6934b0a7f\",\"4e4105a9774c7b5c\",\"917c842a1b46ad4c\"",",\"x\":1264,\"y\":4099,\"w\":912,\"h\":162},{\"id\":\"fa873d7bccd2b6be\",\"type\":\"switch\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"name\":\"Send the image to the correct section of the dashboard\",\"property\":\"filename\",\"propertyType\":\"msg\",\"rules\":",[1107,26172,26173],{},"{\"t\":\"eq\",\"v\":\"ff-logo-wordmark-light_4x.png\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"screen-pseudo-overview-2QvTVle3Mr-384.avif\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"nr-image-1.png\",\"vt\":\"str\"}",",\"checkall\":\"true\",\"repair\":false,\"outputs\":3,\"x\":1550,\"y\":4180,\"wires\":[[\"917c842a1b46ad4c\"],",[1107,26176,26177],{},"\"4e4105a9774c7b5c\"",[1107,26179,26180],{},"\"fb0c76a6934b0a7f\"","]},{\"id\":\"c5e4febe48363987\",\"type\":\"link in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"name\":\"link in 6\",\"links\":",[1107,26183,26184],{},"\"2d29c953bcdab787\"",",\"x\":1305,\"y\":4180,\"wires\":[[\"fa873d7bccd2b6be\"]]},{\"id\":\"d80e5a54a7c5b2f8\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"name\":\"link out 7\",\"mode\":\"link\",\"links\":",[1107,26187,26188],{},"\"c9de7384c9ca672f\"",",\"x\":2135,\"y\":4180,\"wires\":",[1107,26191],{},"},{\"id\":\"fb0c76a6934b0a7f\",\"type\":\"ui-template\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"group\":\"ec62d482d77f7908\",\"page\":\"\",\"ui\":\"\",\"name\":\"Display the image on the Dashboard\",\"order\":6,\"width\":\"3\",\"height\":\"1\",\"head\":\"\",\"format\":\"",[17139,26194,24295,26195,26203],{},[17286,26196,26197,26198,26200,26201,24295],{},"\\n        \u003Cimg ",[23249,26199],{},"= \"msg.paylaod\" alt=\"Image loaded from the filestore\" style=\"width:100%\">",[5368,26202],{},"\\n","\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1930,\"y\":4220,\"wires\":[[\"d80e5a54a7c5b2f8\"]]},{\"id\":\"4e4105a9774c7b5c\",\"type\":\"ui-template\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"group\":\"ec62d482d77f7908\",\"page\":\"\",\"ui\":\"\",\"name\":\"Display the image on the Dashboard\",\"order\":5,\"width\":\"3\",\"height\":\"1\",\"head\":\"\",\"format\":\"",[17139,26206,24295,26207,26203],{},[17286,26208,26197,26209,26200,26211,24295],{},[23249,26210],{},[5368,26212],{},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1930,\"y\":4180,\"wires\":[[\"d80e5a54a7c5b2f8\"]]},{\"id\":\"917c842a1b46ad4c\",\"type\":\"ui-template\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"group\":\"ec62d482d77f7908\",\"page\":\"\",\"ui\":\"\",\"name\":\"Display the image on the Dashboard\",\"order\":2,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"",[17139,26215,24295,26216,26203],{},[17286,26217,26197,26218,26200,26220,24295],{},[23249,26219],{},[5368,26221],{},"\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1930,\"y\":4140,\"wires\":[[\"d80e5a54a7c5b2f8\"]]},{\"id\":\"ec62d482d77f7908\",\"type\":\"ui-group\",\"name\":\"Default\",\"page\":\"550c9ac3b2ed01c9\",\"width\":\"6\",\"height\":\"1\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"550c9ac3b2ed01c9\",\"type\":\"ui-page\",\"name\":\"Home\",\"ui\":\"25f447d87d1ce5c9\",\"path\":\"\u002F\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"c68088445147719b\",\"breakpoints\":",[1107,26224,26225],{},"{\"name\":\"Default\",\"px\":0,\"cols\":3},{\"name\":\"Tablet\",\"px\":576,\"cols\":6},{\"name\":\"Small Desktop\",\"px\":768,\"cols\":9},{\"name\":\"Desktop\",\"px\":1024,\"cols\":12}",",\"order\":1,\"className\":\"\",\"visible\":true,\"disabled\":false},{\"id\":\"25f447d87d1ce5c9\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"\u002Fdashboard\",\"includeClientData\":true,\"acceptsClientConfig\":",[1107,26228,2560],{},",\"showPathInSidebar\":false,\"showPageTitle\":false,\"titleBarStyle\":\"default\"},{\"id\":\"c68088445147719b\",\"type\":\"ui-theme\",\"name\":\"Theme Name\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"}},{\"id\":\"1e54acecec6bd411\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Output a debug once all images have been processed\",\"style\":{\"label\":true},\"nodes\":",[1107,26231,26232],{},"\"c9de7384c9ca672f\",\"b073cd4970e6ddf2\",\"c1b52f44fd4260b3\"",",\"x\":1254,\"y\":4279,\"w\":392,\"h\":82},{\"id\":\"c9de7384c9ca672f\",\"type\":\"link in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"1e54acecec6bd411\",\"name\":\"link in 7\",\"links\":",[1107,26235,26236],{},"\"d80e5a54a7c5b2f8\"",",\"x\":1295,\"y\":4320,\"wires\":[[\"b073cd4970e6ddf2\"]]},{\"id\":\"b073cd4970e6ddf2\",\"type\":\"join\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"1e54acecec6bd411\",\"name\":\"\",\"mode\":\"auto\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\n\",\"joinerType\":\"str\",\"accumulate\":\"false\",\"timeout\":\"\",\"count\":\"\",\"reduceRight\":false,\"x\":1380,\"y\":4320,\"wires\":[[\"c1b52f44fd4260b3\"]]},{\"id\":\"c1b52f44fd4260b3\",\"type\":\"debug\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"1e54acecec6bd411\",\"name\":\"debug 141\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1530,\"y\":4320,\"wires\":",[1107,26239],{},"},{\"id\":\"99aaee673fc70bc2\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Inject the image files' names\",\"style\":{\"label\":true},\"nodes\":",[1107,26242,26243],{},"\"11787d1ab6c8a9ec\",\"49b8f3cce48dd5a9\",\"a9958e1e4b07191a\",\"c452f250e1303c69\",\"08b30802899573a6\"",",\"x\":1254,\"y\":3759,\"w\":782,\"h\":122},{\"id\":\"11787d1ab6c8a9ec\",\"type\":\"inject\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"Inject\",\"props\":",[1107,26246],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":\"1\",\"topic\":\"\",\"x\":1350,\"y\":3800,\"wires\":[[\"c452f250e1303c69\"]]},{\"id\":\"49b8f3cce48dd5a9\",\"type\":\"split\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"\",\"splt\":\"\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"property\":\"payload\",\"x\":1910,\"y\":3800,\"wires\":[[\"a9958e1e4b07191a\"]]},{\"id\":\"a9958e1e4b07191a\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"link out 4\",\"mode\":\"link\",\"links\":",[1107,26249,26250],{},"\"2836914f9b643e34\"",",\"x\":1995,\"y\":3800,\"wires\":",[1107,26253],{},"},{\"id\":\"c452f250e1303c69\",\"type\":\"change\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"Image file names as an array\",\"rules\":",[1107,26256,26257,26258,26261],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"",[1107,26259,26260],{},"\"ff-logo-wordmark-light_4x.png\",\"screen-pseudo-overview-2QvTVle3Mr-384.avif\",\"nr-image-1.png\"","\",\"tot\":\"json\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1720,\"y\":3800,\"wires\":[[\"49b8f3cce48dd5a9\"]]},{\"id\":\"08b30802899573a6\",\"type\":\"ui-event\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"ui\":\"25f447d87d1ce5c9\",\"name\":\"Update images on dashboard open\",\"x\":1420,\"y\":3840,\"wires\":[[\"c452f250e1303c69\"]]}",[15,26264,26265],{},"I have also included some simple dashboard elements you can view alongside the images. Let's take a look at the dashboard:",[15,26267,26268],{},[165,26269],{"alt":26270,"src":26271,"title":26270},"The dashboard showing our images alongside other standard elements","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fthe-dashboard.gif",[15,26273,26274],{},"If you import these flows into Node-RED, you should see the images automatically loaded into the dashboard when you view it. You can also replace the URLs and file paths to try using some different images if you'd like to.",[31,26276,26278],{"id":26277},"more-things-to-try","More things to try",[15,26280,26281],{},"In this example, the images are static but it's simple to load images depending on the state of the flow. As mentioned in this article's introduction, you could display context aware images guiding the user of the dashboard to a specific location on a map, to complete a maintenance task. If you're interested in seeing examples of dynamic image loading please comment below.",[31,26283,1425],{"id":1424},[15,26285,26286],{},"Images can significantly enhance dashboards, but ensuring their proper display in different Node-RED hosting environments, especially within DevOps pipelines, can be challenging. The techniques discussed here enable effective use of images in dashboards, even within containerized setups. Additionally, if you are using FlowFuse, the new features simplify adding and managing static assets.\nI'd love to hear your comments and suggestions on this article. please tell us what you think about this article, and how you might use these techniques in the comments section below.",[2582,26288,26289],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":26291},[26292,26293,26305,26306,26307,26308,26309,26310,26311,26312],{"id":819,"depth":294,"text":820},{"id":25504,"depth":107,"text":25505,"children":26294},[26295],{"id":25516,"depth":294,"text":25517,"children":26296},[26297,26298,26299,26300,26301,26302,26303,26304],{"id":25521,"depth":1868,"text":25522},{"id":25536,"depth":1868,"text":25537},{"id":25552,"depth":1868,"text":25553},{"id":25567,"depth":1868,"text":25568},{"id":25580,"depth":1868,"text":25581},{"id":25604,"depth":1868,"text":25605},{"id":25640,"depth":1868,"text":25641},{"id":25841,"depth":1868,"text":25842},{"id":25916,"depth":107,"text":25917},{"id":25926,"depth":107,"text":25927},{"id":25933,"depth":107,"text":25934},{"id":25957,"depth":107,"text":25958},{"id":26005,"depth":107,"text":26006},{"id":26017,"depth":107,"text":26018},{"id":26277,"depth":107,"text":26278},{"id":1424,"depth":107,"text":1425},{"type":748,"title":26314,"description":26315},"Start Building Better Node-RED Dashboards","Sign up for FlowFuse and use the Static Asset Service to easily add and manage images in your Node-RED dashboards, no filesystem access required.","2023-07-21","Learn to enhance Node-RED dashboards with images using FlowFuse. Pull images from URLs, store locally, and serve them in your dashboards.","blog\u002F2023\u002F07\u002Fimages\u002Fadd-images-in-node-red-header.png",{"keywords":26320,"excerpt":26321},"node-red dashboard images, node-red static assets, flowfuse static asset service, base64 image node-red, node-red image display, dashboard image upload, node-red docker images",{"type":12,"value":26322},[26323],[15,26324,25455],{},"\u002Fblog\u002F2023\u002F07\u002Fimages-in-node-red-dashboards",{"title":25449,"description":26317},{"loc":26325},"blog\u002F2023\u002F07\u002Fimages-in-node-red-dashboards","Import your images into your Node-RED dashboards, wherever you are running your instances",[8650,10,138,12631,7457],"This guide covers multiple methods for displaying images in Node-RED dashboards, including pulling from URLs, encoding files as base64, and serving them via FlowFuse's Static Asset Service. The Static Asset Service approach is the simplest option for containerized environments like Docker or Kubernetes where writing to the local filesystem is not practical.","NUoZ4VyBD5HJgjU5f1aRVQsgRJCDloMl8-mRvrvKhjo",{"id":26334,"title":26335,"authors":26336,"body":26338,"cta":3,"date":26892,"description":26893,"extension":116,"image":26894,"lastUpdated":26895,"meta":26896,"navigation":129,"path":26902,"seo":26903,"sitemap":26904,"stem":26905,"subtitle":26906,"tags":26907,"tldr":3,"video":3,"__hash__":26908},"blog\u002Fblog\u002F2023\u002F07\u002Finfluxdb-historical-data.md","Creating a Historical Data Dashboard with InfluxDB and Node-RED",[26337],"andrew-lynch",{"type":12,"value":26339,"toc":26885},[26340,26343,26346,26349,26356,26358,26361,26367,26371,26380,26383,26386,26393,26395,26398,26405,26407,26410,26417,26419,26422,26429,26431,26434,26437,26440,26447,26451,26459,26466,26469,26476,26479,26486,26493,26496,26503,26506,26513,26516,26520,26528,26535,26537,26540,26546,26553,26560,26562,26570,26577,26579,26586,26593,26595,26598,26605,26607,26610,26617,26619,26623,26626,26629,26636,26639,26646,26657,26664,26666,26669,26676,26679,26686,26688,26699,26706,26708,26711,26718,26724,26727,26734,26736,26739,26746,26748,26751,26758,26760,26767,26772,26774,26777,26784,26786,26789,26794,26796,26799,26806,26808,26828,26833,26835,26838,26845,26848,26850,26853,26856,26862,26879,26882],[15,26341,26342],{},"Every new dashboard is met with the fast-following request, “can we save this data and somehow look back on it?”  Yes, you can, and let’s use InfluxDB to make it happen!",[15,26344,26345],{},"Edge devices are often polling sensors at regular intervals and are a perfect candidate to be paired with a database purpose-built for time-series data, like InfluxDB.  Let’s capture some data, create a live chart, store the data, and then create a GUI for retrieving the data.",[15,26347,26348],{},"Here’s a screenshot of the dashboard we will create, which is divided into two sections. The first section displays live data, while the second section consists of fields that enable users to query the database and retrieve historical data.  Looking at the live data, the chart depicts a sinusoidal graph that represents the scale measurements used for quality assurance in the aggregate production process at an automated mining operation. The graph showcases fluctuations in weight over time, indicating variations in the samples being weighed. This monitoring process ensures the quality and consistency of the aggregates being produced.  The historical data shows a snippet of this information that was retrieved from InfluxDB.",[15,26350,26351],{},[165,26352],{"alt":26353,"src":26354,"title":26355},"\"Screenshot showing the historical dashboard\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fdashboard-1.png","Screenshot showing the historical dashboard",[5368,26357],{},[15,26359,26360],{},"Here is a screenshot of the simple Node-RED flow to create that dashboard.  We will dive into the details through this article, and, by the end, you will be able to create this flow yourself.",[15,26362,26363],{},[165,26364],{"alt":26365,"src":26366,"title":26365},"Screenshot showing the historical dashboard flow","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fflow-2.png",[31,26368,26370],{"id":26369},"capturing-serial-port-data","Capturing serial port data",[15,26372,26373,26374,26379],{},"The live view is fed by data coming from a simple scale with a serial interface.  This ",[63,26375,26378],{"href":26376,"rel":26377},"https:\u002F\u002Fwww.brecknellscales.com\u002Fwp-content\u002Fuploads\u002F2022\u002F09\u002FLPS-Series_u_en_fr_501724-1.pdf",[67],"Brecknell LPS-150"," scale will auto power-on, remembers the last tare setting, and continuously sends its reading via RS-232, so it is a great unit to use for unattended IoT projects.",[15,26381,26382],{},"On the Node-RED side, a serial node can be configured to capture this incoming data.  If your device running Node-RED doesn’t have an RS-232 port, there are many variations of RS-232-to-USB cables to help you connect.  This scale is sending data at a very high-speed interval so it is important to use a “delay” node before the rest of your flow gets bogged down.",[15,26384,26385],{},"Below, I have configured the serial port node with the same settings that were used to set up the scale. These settings are commonly documented as \"9600 8N1\" in shorthand.  In serial communication it is necessary for the two devices to have the exact same settings or the data becomes garbled.  The incoming stream of ASCII text is divided using the hex value 0x0D, which corresponds to the return character. This character is used as a delimiter to separate the individual chunks of text within the incoming data stream.",[15,26387,26388],{},[165,26389],{"alt":26390,"src":26391,"title":26392},"\"Screenshot showing the serial port node config\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fserial-port-node-3.png","Screenshot showing the serial port node config",[5368,26394],{},[15,26396,26397],{},"With this “delay” node, we now have a new message from the scale at a rate of 1 msg per 5 seconds",[15,26399,26400],{},[165,26401],{"alt":26402,"src":26403,"title":26404},"\"Screenshot showing the delay node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fdelay-4.png","Screenshot showing the delay node properties tab",[5368,26406],{},[15,26408,26409],{},"The debugger allows us to see the raw data as it is captured.",[15,26411,26412],{},[165,26413],{"alt":26414,"src":26415,"title":26416},"\"Debugger\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fdebugger-5.png","Debugger",[5368,26418],{},[15,26420,26421],{},"Unfortunately, these values are not in a friendly form to work with.  Ideally, we want our payload to just be a number, not this string with odd characters, extra spaces, and the units.",[15,26423,26424],{},[165,26425],{"alt":26426,"src":26427,"title":26428},"\"Screenshot showing the change node setting payload\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchange-node-6.png","Screenshot showing the change node setting payload",[5368,26430],{},[15,26432,26433],{},"We need to extract the numeric part of the string using a regular expression with a “change” node and the JSONata expression $number($match(msg.payload,  \u002F-?(\\d+(.\\d+)?)\u002F , 10).match).  “$match” and “\u002F-?(\\d+(.\\d+)?)\u002F” help the function pull out the numeric components of the string and “$number” parses these components to be an actual number data type.",[15,26435,26436],{},"Here are the properties of the “change” node.",[15,26438,26439],{},"When we look in the debugger we see the payload specified as a “number” and the value displayed in blue, both indications that we have successfully extracted the weight as the correct data type.",[15,26441,26442],{},[165,26443],{"alt":26444,"src":26445,"title":26446},"\"Screenshot showing the output type in the debug panel\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fnumber-7.png","Screenshot showing the output type in the debug panel",[31,26448,26450],{"id":26449},"setting-up-serverless-influxdb-in-the-cloud","Setting up serverless InfluxDB in the cloud",[15,26452,26453,26454,26458],{},"Now we have some live data, let’s store it using InfluxDB. Below are the steps to set up an account with the InfluxDB free service.  Navigate to ",[63,26455,26456],{"href":26456,"rel":26457},"https:\u002F\u002Fwww.influxdata.com\u002Fproducts\u002Finfluxdb-overview\u002F",[67]," and let’s begin.  Click on “Get Started for Free” under Cloud, InfluxDB Cloud Serverless.",[15,26460,26461],{},[165,26462],{"alt":26463,"src":26464,"title":26465},"\"Screenshot showing the Influxdb cloud 'Getting started' button\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Frun-influxdb-8.png","Screenshot showing the Influxdb cloud 'Getting started' button",[15,26467,26468],{},"For this example the Free plan will work fine.",[15,26470,26471],{},[165,26472],{"alt":26473,"src":26474,"title":26475},"\"Screenshot showing the influxdb cloud tiers\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ffree-influxdb-9.png","Screenshot showing the influxdb cloud tiers",[15,26477,26478],{},"Create a bucket to store the data.",[15,26480,26481],{},[165,26482],{"alt":26483,"src":26484,"title":26485},"\"Screenshot showing the 'Go to buckets' button\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fbucket-influxdb-10.png","Screenshot showing the 'Go to buckets' button",[15,26487,26488],{},[165,26489],{"alt":26490,"src":26491,"title":26492},"\"Screenshot showing the 'Create Bucket' button\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fload-data-influxdb-11.png","Screenshot showing the 'Create Bucket' button",[15,26494,26495],{},"Generate a token to direct the calls from Node-RED to your InfluxDB account when they hit the InfluxDB server:",[15,26497,26498],{},[165,26499],{"alt":26500,"src":26501,"title":26502},"\"Screenshot showing the 'Genrate token' button\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ftoken-influxdb-12.png","Screenshot showing the 'Genrate token' button",[15,26504,26505],{},"I selected “Generate All Access API Token,” but eventually you will want a custom, more restricted approach.",[15,26507,26508],{},[165,26509],{"alt":26510,"src":26511,"title":26512},"\"Screenshot showing the 'prompt' asing to enter the description for token\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fgenerate-token-influxdb-13.png","Screenshot showing the 'prompt' asing to enter the description for token",[15,26514,26515],{},"Copy your token and do not share it!  (mine will be deleted later)",[31,26517,26519],{"id":26518},"connecting-node-red-to-influxdb","Connecting Node-RED to InfluxDB",[15,26521,26522,26523,26527],{},"Navigate to “Manage Palette” in the Node-RED hamburger menu in the upper right corner of the flow editor.  I did a search for InfluxDB and selected the most popular one, “node-red-contrib-influxdb” by looking at the number of downloads per week at ",[63,26524,26525],{"href":26525,"rel":26526},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-influxdb",[67],".  When you are just starting out, it can be a smart decision to go with the popular option. The popularity indicates a level of trust and adoption within the community, making it a reliable choice for beginners",[15,26529,26530],{},[165,26531],{"alt":26532,"src":26533,"title":26534},"\"Screenshot showing the influxdb node in the manage palette\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Finstall-contrib-influxdb-14.png","Screenshot showing the influxdb node in the manage palette",[5368,26536],{},[15,26538,26539],{},"After installing this package you will see three new nodes in your flow editor.",[15,26541,26542],{},[165,26543],{"alt":26544,"src":26545,"title":26544},"Screenshot showing the installed influxdb nodes in the palette","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Finfluxdb-nodes-15.png",[15,26547,26548,26549],{},"Drag and drop the “influxdb out” node into your flow, double click on it, and start filling out the needed fields.  The naming convention of “test\u003C",[26550,26551,26552],"thing",{},">” works well for initial setups to make it clear what names should go where.",[15,26554,26555],{},[165,26556],{"alt":26557,"src":26558,"title":26559},"\"Screenshot showing the influxdb-out node config\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Finfluxdb-out-node-16.png","Screenshot showing the influxdb-out node config",[5368,26561],{},[15,26563,26564,26565,26569],{},"It was a little unclear what URL to use with this serverless option, but I guessed it was the same as the URL for the InfluxDB resource center account page, “",[63,26566,26567],{"href":26567,"rel":26568},"https:\u002F\u002Fus-east-1-1.aws.cloud2.influxdata.com\u002F",[67],"” and it worked.  Then, enter the API token that was generated earlier.",[15,26571,26572],{},[165,26573],{"alt":26574,"src":26575,"title":26576},"\"image_tooltip\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Finfluxdb-node-17.png","image_tooltip",[5368,26578],{},[15,26580,26581,26582,26585],{},"The “influxdb out” node is now ready to start storing payloads.  The documentation for the InfluxDB nodes at ",[63,26583,26525],{"href":26525,"rel":26584},[67]," gives more detail as to extra options, such as tags, that you might want to attach to your data being stored.  In this simple example, we are just going to send the “influxdb out” node a number via the msg.payload.",[15,26587,26588],{},[165,26589],{"alt":26590,"src":26591,"title":26592},"Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fflow-influxdb-out-18.png","Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget",[5368,26594],{},[15,26596,26597],{},"Here is a chart of the live data which is also being stored.",[15,26599,26600],{},[165,26601],{"alt":26602,"src":26603,"title":26604},"\"Screenshot showing the dashboard with live data chart\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Flive-data-chart-19.png","Screenshot showing the dashboard with live data chart",[5368,26606],{},[15,26608,26609],{},"The InfluxDB Data Explorer helps you create a SQL call and allows you to run it right in the browser so you can verify that your data is being stored correctly.",[15,26611,26612],{},[165,26613],{"alt":26614,"src":26615,"title":26616},"\"Screenshot showing the InfluxDB Explorer\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Finfluxdb-explorer-20.png","Screenshot showing the InfluxDB Explorer",[5368,26618],{},[31,26620,26622],{"id":26621},"creating-a-historical-data-gui","Creating a historical data GUI",[15,26624,26625],{},"Now we have our data being stored, but we aren’t quite finished. We still want an easy way to pull this information up and for it to be presented in a chart, just like the live data.",[15,26627,26628],{},"Here is the Dashboard group we will create for this GUI.",[15,26630,26631],{},[165,26632],{"alt":26633,"src":26634,"title":26635},"\"Screenshot showing the GUI for historical data\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fhistorical-data-21.png","Screenshot showing the GUI for historical data",[15,26637,26638],{},"And here is the flow to create it.",[15,26640,26641],{},[165,26642],{"alt":26643,"src":26644,"title":26645},"\"Screenshot showing the flow of historical data GUI.\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fdata-flow-22.png","Screenshot showing the flow of historical data GUI",[15,26647,26648,26649,26652,26653,29],{},"A “template” node creates a convenient way to create a plain text output with variable properties within.  Below you can see that ",[924,26650,26651],{},"msg.query"," is created from a string of text with “rangeStart” and “rangeEnd” dynamically inserted using the “mustache” syntax.  More information about how to query InfluxDB can be found here:  ",[63,26654,26655],{"href":26655,"rel":26656},"https:\u002F\u002Fdocs.influxdata.com\u002Finfluxdb\u002Fv2.0\u002Fquery-data\u002Fget-started\u002Fquery-influxdb\u002F",[67],[15,26658,26659],{},[165,26660],{"alt":26661,"src":26662,"title":26663},"\"Screenshot showing the template node\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ftemplate-node-23.png","Screenshot showing the template node",[5368,26665],{},[15,26667,26668],{},"Using the \"Form\" dashboard node is an easy way to collect all the required information for our query. We need to be able to enter in a date and time to start gathering the data, and a window to know how long a range of values to pull.",[15,26670,26671],{},[165,26672],{"alt":26673,"src":26674,"title":26675},"\"Screenshot of the form widget\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fform-node-24.png","Screenshot of the form widget",[15,26677,26678],{},"Here is the code from the “time\u002Fdate” function node.  A bit of juggling of local time versus UTC time is needed to allow the user to intuitively query the correct data for their timezone.",[15,26680,26681],{},[165,26682],{"alt":26683,"src":26684,"title":26685},"\"Screenshot of the function node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ffunction-node-25.png","Screenshot of the function node properties tab",[5368,26687],{},[15,26689,26690,26691,26694,26695,26698],{},"Here is the “change” node used to create the msg.rangeEnd.  The JSONatta expression is ",[924,26692,26693],{},"$fromMillis($toMillis(msg.rangeStart) + msg.payload.window * 60 * 1000)",".  The expression combines the milliseconds from the ",[924,26696,26697],{},"msg.rangeStart"," with the calculated milliseconds in the “Window (minutes)” from the GUI.",[15,26700,26701],{},[165,26702],{"alt":26703,"src":26704,"title":26705},"\"Screenshot of the change node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchange-node-26.png","Screenshot of the change node properties tab",[5368,26707],{},[15,26709,26710],{},"Now that the query is coming back from InfluxDB, let’s break down how to transform this data object into one that can be read by the “chart” node.  Below, we see on the left column what the object looks like from InfluxDB and on the right we see how it must be structured to be viewed in the chart.",[15,26712,26713],{},[165,26714],{"alt":26715,"src":26716,"title":26717},"\"Screenshot showing the formated data in the debug panel\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fdata-format-27.png","Screenshot showing the formated data in the debug panel",[15,26719,26720,26721,29],{},"Rob Marcer has a great article on working with persistent chart data found here: ",[63,26722,26723],{"href":26723},"\u002Fblog\u002F2023\u002F05\u002Fpersisting-chart-data-in-node-red\u002F",[15,26725,26726],{},"We can use a series of nodes from the Node-RED core package to transform this data.",[15,26728,26729],{},[165,26730],{"alt":26731,"src":26732,"title":26733},"\"Screenshot showing the flow to transform the data\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ftransform-data-28.png","Screenshot showing the flow to transform the data",[5368,26735],{},[15,26737,26738],{},"First, a “switch” node is used to determine if the response InfluxDB contains any data so that we can either format the data properly, or clear the chart and indicate “No Data.”",[15,26740,26741],{},[165,26742],{"alt":26743,"src":26744,"title":26745},"\"Screenshot of the switch node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fswitch-node-28.png","Screenshot of the switch node properties tab",[5368,26747],{},[15,26749,26750],{},"The “Label” field in the “chart” node can also be dynamically created with the mustache syntax.",[15,26752,26753],{},[165,26754],{"alt":26755,"src":26756,"title":26757},"\"Screenshot of the chart widget setting label dynamically\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchart-node-29.png","Screenshot of the chart widget setting label dynamically",[5368,26759],{},[15,26761,26762,26763,26766],{},"If the “is not empty” “switch” node sees an empty payload, this “change” node sets the payload to an empty array, clearing the chart, and sets the ",[924,26764,26765],{},"msg.title"," to “No Data” so users know their query, though successful, returned an empty set of values.",[15,26768,26769],{},[165,26770],{"alt":26703,"src":26771,"title":26705},"\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchange-node-30.png",[5368,26773],{},[15,26775,26776],{},"The parameters for the “split” node can be left as-is.",[15,26778,26779],{},[165,26780],{"alt":26781,"src":26782,"title":26783},"\"Screenshot of the split node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fsplit-node-31.png","Screenshot of the split node properties tab",[5368,26785],{},[15,26787,26788],{},"In the “chartData” “change” node, will pull out the two values we need for the chart, milliseconds since the UNIX epoch for the x-value and the measurement from the scale for the y-value.  A simple JSONatta expression helps us transform the date from a string to milliseconds for the x-value.",[15,26790,26791],{},[165,26792],{"alt":26703,"src":26793,"title":26705},"\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchange-node-32.png",[5368,26795],{},[15,26797,26798],{},"The “join” node just needs to be set to “Combine each” msg.chartData object and configured “to create” an array.",[15,26800,26801],{},[165,26802],{"alt":26803,"src":26804,"title":26805},"\"Screenshot of the join node properties tab\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fjoin-node-33.png","Screenshot of the join node properties tab",[5368,26807],{},[15,26809,26810,26811,26820,26821,26824,26825,26827],{},"The final “change” node, “format,” is where we prescribe the format needed for the “chart” node, ",[1107,26812,26813,26814,26817,26818,4957],{},"{\"series\":",[1107,26815,26816],{},"\"\"",",\"data\":[[]],\"labels\":",[1107,26819,26816],{},", and finally we insert our ",[924,26822,26823],{},"msg.chartData"," array into that structure.  Notice ",[924,26826,26765],{}," is now set to “Data Received.”",[15,26829,26830],{},[165,26831],{"alt":26703,"src":26832,"title":26705},"\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Fchange-node-33.png",[5368,26834],{},[15,26836,26837],{},"And, there you have it.  You can query the same range of data found on the live chart to ensure the code is working and then you can use the dashboard to pull up historical data, way in the past from what is shown on the live chart.",[15,26839,26840],{},[165,26841],{"alt":26842,"src":26843,"title":26844},"\"Screenshot of the dashboard showing the historical and live data chart\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Finfluxdb-historical-data\u002Ffinal-dashboard-34.png","Screenshot of the dashboard showing the historical and live data chart",[15,26846,26847],{},"Your current version is clear and concise, great for a blog or call-to-action section. Here's a slightly refined version with improved flow, punctuation, and tone for polish, while keeping the structure intact:",[31,26849,14042],{"id":14041},[15,26851,26852],{},"You’ve successfully built a powerful dashboard that captures real-time data and stores it in InfluxDB for historical analysis. This setup provides valuable insights into your process data and helps identify trends over time.",[15,26854,26855],{},"If you are planning to run this dashboard in production, a few important questions may come to mind: How do you ensure it runs reliably 24\u002F7? How do you deploy it to other locations? What happens if someone accidentally breaks the flow?",[15,26857,26858,26859,26861],{},"This is where ",[19,26860,803],{}," comes in. It takes your existing Node-RED flows and adds:",[39,26863,26864,26867,26870,26873,26876],{},[42,26865,26866],{},"Automatic backups and instant recovery",[42,26868,26869],{},"Easy deployment to multiple locations",[42,26871,26872],{},"Version control for safe and trackable updates",[42,26874,26875],{},"Team collaboration without conflicts",[42,26877,26878],{},"Remote instance management, and much more",[15,26880,26881],{},"Your flows continue to work just as they are, FlowFuse simply makes them production-ready.",[15,26883,26884],{},"[Try FlowFuse free for 30 days]({% include \"sign-up-url.njk\" %}) and see how it transforms Node-RED into a scalable, enterprise-ready platform.",{"title":103,"searchDepth":104,"depth":104,"links":26886},[26887,26888,26889,26890,26891],{"id":26369,"depth":107,"text":26370},{"id":26449,"depth":107,"text":26450},{"id":26518,"depth":107,"text":26519},{"id":26621,"depth":107,"text":26622},{"id":14041,"depth":107,"text":14042},"2023-07-18","Discover how to build a Historical Data Dashboard with InfluxDB and Node-RED. Capture, store, and visualize data for insightful analysis.","blog\u002F2023\u002F07\u002Fimages\u002Fhistorical-data-dashboard.png","2025-07-14",{"keywords":26897,"excerpt":26898},"Node-RED InfluxDB, Time-series data, Industrial dashboard, Historical data visualization, IoT dashboard, Serial port sensor data,  Live and historical charts",{"type":12,"value":26899},[26900],[15,26901,26342],{},"\u002Fblog\u002F2023\u002F07\u002Finfluxdb-historical-data",{"title":26335,"description":26893},{"loc":26902},"blog\u002F2023\u002F07\u002Finfluxdb-historical-data","Detailed instructions on how to create a Node-RED dashboard that shows historical data.",[8650,138],"pYwskr7Aw7ow9D3cpiCNXqu47jMwcrSTAfD0tDAqpeM",{"id":26910,"title":26911,"authors":26912,"body":26913,"cta":28734,"date":28737,"description":28738,"extension":116,"image":28739,"lastUpdated":6203,"meta":28740,"navigation":129,"path":28746,"seo":28747,"sitemap":28748,"stem":28749,"subtitle":28750,"tags":28751,"tldr":28752,"video":3,"__hash__":28753},"blog\u002Fblog\u002F2023\u002F07\u002Fhow-to-deploy-a-basic-opc-ua-server-in-node-red.md","How to Deploy a Basic OPC-UA Server in Node-RED - Part 1 (2026)",[24025],{"type":12,"value":26914,"toc":28727},[26915,26918,26922,26925,26928,26943,26947,26962,26971,26974,26981,26989,26997,27002,27005,27012,27026,27029,27033,27046,27055,27058,27064,27081,27333,27348,27597,27600,27607,27614,27621,27644,27650,27656,27665,27672,27678,27688,27691,27720,27723,27749,27760,27927,27930,28077,28080,28397,28400,28608,28615,28635,28642,28648,28652,28661,28664,28676,28679,28693,28699,28710,28716,28718,28721,28724],[15,26916,26917],{},"This article is the first part of a series of OPC-UA content.  Here, we will explain some basic concepts of OPC-UA as they apply to building a server in Node-RED, then walk through and deploy an example OPC-UA Server.",[31,26919,26921],{"id":26920},"what-is-opc-ua","What is OPC-UA?",[15,26923,26924],{},"Open Platform Communications Unified Architecture (OPC UA) is an open, platform independent communication framework frequently utilized in industrial automation, and is considered one of the key protocol standards for Industry 4.0 and Industrial IoT (IIoT).  The standard is developed and maintained by a consortium called the OPC Foundation, with recognizable industry names such as Siemens, Honeywell, Microsoft, Beckhoff, SAP, Yokogawa, ABB, Rockwell, and Schneider Electric.",[15,26926,26927],{},"Because of OPC-UA’s wide industry acceptance, it is increasingly becoming natively supported on devices and systems spanning the entirety of the automation pyramid.",[15,26929,26930,26935],{},[165,26931],{"alt":26932,"src":26933,"title":26934},"\"Automation Pyramid\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fautomation-pyramid.jpg","Automation Pyramid",[170,26936,26937,26938],{},"Image reference - ",[63,26939,26942],{"href":26940,"rel":26941},"https:\u002F\u002Fwww.motioncontroltips.com\u002Fwhat-is-opc-ua-and-how-does-it-compare-with-industrial-ethernet\u002F",[67],"imagecontroltips.com",[31,26944,26946],{"id":26945},"fieldbus-model-vs-opc-ua-information-model","Fieldbus Model vs OPC-UA Information Model",[15,26948,26949,26950,26955],{},"As of today, industrial ethernet fieldbuses dominate the field\u002Fdevice-level (level 0) and controller\u002FPLC-level (level 1) of the automation pyramid.\n",[165,26951],{"alt":26952,"src":26953,"title":26954},"\"OPC-UA Pyramid\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002FOPC-UA-pyramid-2.webp","OPC-UA Pyramid",[170,26956,26937,26957],{},[63,26958,26961],{"href":26959,"rel":26960},"https:\u002F\u002Fwww.mdpi.com\u002F1424-8220\u002F21\u002F14\u002F4656",[67],"mdpi.com",[15,26963,26964,26965,26970],{},"Fieldbuses such as Profinet, Ethernet\u002FIP, and EtherCAT, employ deterministic, real-time communication, which is essential for mission-critical and safety-oriented automation tasks.  OPC-UA is most commonly encountered at the SCADA level and above (level 2-4).  However, with the inclusion of ",[63,26966,26969],{"href":26967,"rel":26968},"https:\u002F\u002Fwww.tttech-industrial.com\u002Fresource-library\u002Fblog-posts\u002Fopc-ua-fx",[67],"Time Sensitive Networking (TSN) into the OPC-UA technology stack",", OPC-UA can be feasibly used for real-time communication all the way down to the device level.",[15,26972,26973],{},"Traditionally, fieldbus protocols transmit only raw data from field devices (ie, a float to represent a pressure, or a boolean to represent the position of a switch).  The fieldbus data gets pushed up the automation stack layer by layer, where eventually it will be converted to a format suitable for IT systems to consume (such as OPC-UA).",[15,26975,26976],{},[165,26977],{"alt":26978,"src":26979,"title":26980},"\"Fieldbus Model\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Ffieldbus-model.png","Fieldbus Model",[15,26982,26983,26984,26988],{},"In contrast to fieldbus protocols, OPC-UA represents automation data in the form of nodes. The framework for constructing nodes is referred to as the ",[63,26985,26987],{"href":26986},"\u002Fblog\u002F2023\u002F07\u002Flhttps:\u002Freference.opcfoundation.org\u002FCore\u002FPart5\u002Fv104\u002Fdocs","OPC Information model",", and consists of pre-defined classes and methods that are programmed in the OPC Server address space.",[15,26990,26991,26996],{},[165,26992],{"alt":26993,"src":26994,"title":26995},"\"OPC Information Model\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-information-model.png","OPC Information Model","\nDevices can be described as objects that give a holistic view of the device, beyond simply the raw value.  To construct a device object, we can take different individual attributes associated with a device, such as the transmitter raw value, transmitter fault flag, alarm setpoint, and combine them, similar to how user-defined datatypes (UDTs) are objects used to represent devices in PLCs.  The information model also defines a folder structure, to allow devices information to reside in a structured hierarchy.  Using the example temperature transmitter above, an example folder structure can be constructed as follows:",[15,26998,26999],{},[924,27000,27001],{},"\u002FRoot\u002FObjects\u002FCalcinator 1 PLC\u002FTemperature Transmitters\u002FTank 1 Temperature\u002FTransmitter Value",[15,27003,27004],{},"This folder structure will be exposed via the OPC Client browser, allowing end-users to easily “drill down” to individual node information in a logical manner.",[15,27006,27007,27011],{},[165,27008],{"alt":27009,"src":27010,"title":26995},"\"OPC Client Browser\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-client-browser.png","\nIn summary, OPC-UA represents a trade-off between complex information modeling, with the versatility for that data to be consumed by devices and systems all the way up the automation pyramid layers.  The data does not have to pass through subsequent automation layers on the way up, nor does the data need to undergo any conversion along the way.",[15,27013,27014,27019],{},[165,27015],{"alt":27016,"src":27017,"title":27018},"\"OPC-UA Distributed Model\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002FOPC-UA-distributed-model.jpg","OPC-UA Distributed Model",[170,27020,26937,27021],{},[63,27022,27025],{"href":27023,"rel":27024},"https:\u002F\u002Fifr.org\u002Fpost\u002Ffaster-robot-communication-through-the-opc-robotics-companion-specification",[67],"ifr.org",[15,27027,27028],{},"The OPC client simply needs to subscribe to the OPC Server endpoint url (ex. opc.tcp:\u002F\u002Fserver.address), and the client will be able to browse the structured OPC data as it’s modeled in the server.  Any client will receive the information in the same manner, regardless if it’s a PLC, SCADA, MES, or ERP system.  This opens the possibility for horizontal and vertical system integration in a standardized manner. Additionally, the more information that is exposed about a device, the easier it is to track, and use said data to autonomously reconfigure, or pre-emptively take maintenance actions.",[31,27030,27032],{"id":27031},"deploying-an-example-opc-ua-server-in-node-red","Deploying an Example OPC-UA Server in Node-RED",[15,27034,27035,27036,27041,27042,27045],{},"With some background on OPC-UA and how information is modeled in mind, we can take a look at the ",[63,27037,27040],{"href":27038,"rel":27039},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-opcua-server",[67],"node-red-contrib-opcua-server"," node, which is merely a compact version of the ",[63,27043,11368],{"href":11366,"rel":27044},[67]," node that only focuses on the OPC-UA server and hence requires less dependencies.",[15,27047,27048,27049,27054],{},"An ",[63,27050,27053],{"href":27051,"rel":27052},"https:\u002F\u002Fgithub.com\u002FBiancoRoyal\u002Fnode-red-contrib-opcua-server\u002Fblob\u002Fmaster\u002Fexamples\u002Fserver-with-context.json",[67],"example flow"," is provided on github that can serve as a basis for understanding how a OPC-UA server is constructed.  Let’s get the example server up and running.",[15,27056,27057],{},"Deploying the example flow yields the following result -",[15,27059,27060],{},[165,27061],{"alt":27062,"src":27063,"title":27062},"Compact Server Flow","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fcompact-server-flow.png",[39,27065,27066],{},[42,27067,27068,27069,27072,27073,27076,27077,27080],{},"an inject node is trigging the function ",[924,27070,27071],{},"set flow context Inputs"," at a one second interval, which creates 7 randomly generated float values and stores them as flow context variables, ",[924,27074,27075],{},"isoInput2"," - ",[924,27078,27079],{},"isoInput8"," (isolated inputs).  The values will change to a new random number each time the node is injected.",[1756,27082,27084],{"className":1758,"code":27083,"language":1760,"meta":103,"style":103},"flow.set('isoInput2', Math.random() + 12.0)\nflow.set('isoInput3', Math.random() + 13.0)\nflow.set('isoInput4', Math.random() + 14.0)\nflow.set('isoInput5', Math.random() + 15.0)\nflow.set('isoInput6', Math.random() + 16.0)\nflow.set('isoInput7', Math.random() + 17.0)\nflow.set('isoInput8', Math.random() + 18.0)\n\n...\n",[924,27085,27086,27121,27155,27189,27223,27257,27291,27324,27328],{"__ignoreMap":103},[1107,27087,27088,27090,27092,27094,27096,27098,27100,27102,27104,27106,27108,27111,27113,27116,27119],{"class":1765,"line":1766},[1107,27089,15228],{"class":1779},[1107,27091,29],{"class":1783},[1107,27093,15357],{"class":15356},[1107,27095,15360],{"class":1779},[1107,27097,15363],{"class":1783},[1107,27099,27075],{"class":1966},[1107,27101,15363],{"class":1783},[1107,27103,1955],{"class":1783},[1107,27105,17805],{"class":1779},[1107,27107,29],{"class":1783},[1107,27109,27110],{"class":15356},"random",[1107,27112,20954],{"class":1779},[1107,27114,27115],{"class":1783},"+",[1107,27117,27118],{"class":1854}," 12.0",[1107,27120,5192],{"class":1779},[1107,27122,27123,27125,27127,27129,27131,27133,27136,27138,27140,27142,27144,27146,27148,27150,27153],{"class":1765,"line":107},[1107,27124,15228],{"class":1779},[1107,27126,29],{"class":1783},[1107,27128,15357],{"class":15356},[1107,27130,15360],{"class":1779},[1107,27132,15363],{"class":1783},[1107,27134,27135],{"class":1966},"isoInput3",[1107,27137,15363],{"class":1783},[1107,27139,1955],{"class":1783},[1107,27141,17805],{"class":1779},[1107,27143,29],{"class":1783},[1107,27145,27110],{"class":15356},[1107,27147,20954],{"class":1779},[1107,27149,27115],{"class":1783},[1107,27151,27152],{"class":1854}," 13.0",[1107,27154,5192],{"class":1779},[1107,27156,27157,27159,27161,27163,27165,27167,27170,27172,27174,27176,27178,27180,27182,27184,27187],{"class":1765,"line":294},[1107,27158,15228],{"class":1779},[1107,27160,29],{"class":1783},[1107,27162,15357],{"class":15356},[1107,27164,15360],{"class":1779},[1107,27166,15363],{"class":1783},[1107,27168,27169],{"class":1966},"isoInput4",[1107,27171,15363],{"class":1783},[1107,27173,1955],{"class":1783},[1107,27175,17805],{"class":1779},[1107,27177,29],{"class":1783},[1107,27179,27110],{"class":15356},[1107,27181,20954],{"class":1779},[1107,27183,27115],{"class":1783},[1107,27185,27186],{"class":1854}," 14.0",[1107,27188,5192],{"class":1779},[1107,27190,27191,27193,27195,27197,27199,27201,27204,27206,27208,27210,27212,27214,27216,27218,27221],{"class":1765,"line":104},[1107,27192,15228],{"class":1779},[1107,27194,29],{"class":1783},[1107,27196,15357],{"class":15356},[1107,27198,15360],{"class":1779},[1107,27200,15363],{"class":1783},[1107,27202,27203],{"class":1966},"isoInput5",[1107,27205,15363],{"class":1783},[1107,27207,1955],{"class":1783},[1107,27209,17805],{"class":1779},[1107,27211,29],{"class":1783},[1107,27213,27110],{"class":15356},[1107,27215,20954],{"class":1779},[1107,27217,27115],{"class":1783},[1107,27219,27220],{"class":1854}," 15.0",[1107,27222,5192],{"class":1779},[1107,27224,27225,27227,27229,27231,27233,27235,27238,27240,27242,27244,27246,27248,27250,27252,27255],{"class":1765,"line":1868},[1107,27226,15228],{"class":1779},[1107,27228,29],{"class":1783},[1107,27230,15357],{"class":15356},[1107,27232,15360],{"class":1779},[1107,27234,15363],{"class":1783},[1107,27236,27237],{"class":1966},"isoInput6",[1107,27239,15363],{"class":1783},[1107,27241,1955],{"class":1783},[1107,27243,17805],{"class":1779},[1107,27245,29],{"class":1783},[1107,27247,27110],{"class":15356},[1107,27249,20954],{"class":1779},[1107,27251,27115],{"class":1783},[1107,27253,27254],{"class":1854}," 16.0",[1107,27256,5192],{"class":1779},[1107,27258,27259,27261,27263,27265,27267,27269,27272,27274,27276,27278,27280,27282,27284,27286,27289],{"class":1765,"line":1896},[1107,27260,15228],{"class":1779},[1107,27262,29],{"class":1783},[1107,27264,15357],{"class":15356},[1107,27266,15360],{"class":1779},[1107,27268,15363],{"class":1783},[1107,27270,27271],{"class":1966},"isoInput7",[1107,27273,15363],{"class":1783},[1107,27275,1955],{"class":1783},[1107,27277,17805],{"class":1779},[1107,27279,29],{"class":1783},[1107,27281,27110],{"class":15356},[1107,27283,20954],{"class":1779},[1107,27285,27115],{"class":1783},[1107,27287,27288],{"class":1854}," 17.0",[1107,27290,5192],{"class":1779},[1107,27292,27293,27295,27297,27299,27301,27303,27305,27307,27309,27311,27313,27315,27317,27319,27322],{"class":1765,"line":1623},[1107,27294,15228],{"class":1779},[1107,27296,29],{"class":1783},[1107,27298,15357],{"class":15356},[1107,27300,15360],{"class":1779},[1107,27302,15363],{"class":1783},[1107,27304,27079],{"class":1966},[1107,27306,15363],{"class":1783},[1107,27308,1955],{"class":1783},[1107,27310,17805],{"class":1779},[1107,27312,29],{"class":1783},[1107,27314,27110],{"class":15356},[1107,27316,20954],{"class":1779},[1107,27318,27115],{"class":1783},[1107,27320,27321],{"class":1854}," 18.0",[1107,27323,5192],{"class":1779},[1107,27325,27326],{"class":1765,"line":1924},[1107,27327,1921],{"emptyLinePlaceholder":129},[1107,27329,27330],{"class":1765,"line":1930},[1107,27331,27332],{"class":1783},"...\n",[39,27334,27335],{},[42,27336,27337,27338,27341,27342,27076,27345,27080],{},"another inject node is triggering the function ",[924,27339,27340],{},"set flow context Outputs",", also at a one second interval, which creates another set of 7 randomly generated float values and stores them as flow context variables, ",[924,27343,27344],{},"isoOutput2",[924,27346,27347],{},"isoOutput8",[1756,27349,27351],{"className":1758,"code":27350,"language":1760,"meta":103,"style":103},"flow.set('isoOutput2', Math.random() + 2.0)\nflow.set('isoOutput3', Math.random() + 3.0)\nflow.set('isoOutput4', Math.random() + 4.0)\nflow.set('isoOutput5', Math.random() + 5.0)\nflow.set('isoOutput6', Math.random() + 6.0)\nflow.set('isoOutput7', Math.random() + 7.0)\nflow.set('isoOutput8', Math.random() + 8.0)\n\n...\n",[924,27352,27353,27386,27420,27454,27488,27522,27556,27589,27593],{"__ignoreMap":103},[1107,27354,27355,27357,27359,27361,27363,27365,27367,27369,27371,27373,27375,27377,27379,27381,27384],{"class":1765,"line":1766},[1107,27356,15228],{"class":1779},[1107,27358,29],{"class":1783},[1107,27360,15357],{"class":15356},[1107,27362,15360],{"class":1779},[1107,27364,15363],{"class":1783},[1107,27366,27344],{"class":1966},[1107,27368,15363],{"class":1783},[1107,27370,1955],{"class":1783},[1107,27372,17805],{"class":1779},[1107,27374,29],{"class":1783},[1107,27376,27110],{"class":15356},[1107,27378,20954],{"class":1779},[1107,27380,27115],{"class":1783},[1107,27382,27383],{"class":1854}," 2.0",[1107,27385,5192],{"class":1779},[1107,27387,27388,27390,27392,27394,27396,27398,27401,27403,27405,27407,27409,27411,27413,27415,27418],{"class":1765,"line":107},[1107,27389,15228],{"class":1779},[1107,27391,29],{"class":1783},[1107,27393,15357],{"class":15356},[1107,27395,15360],{"class":1779},[1107,27397,15363],{"class":1783},[1107,27399,27400],{"class":1966},"isoOutput3",[1107,27402,15363],{"class":1783},[1107,27404,1955],{"class":1783},[1107,27406,17805],{"class":1779},[1107,27408,29],{"class":1783},[1107,27410,27110],{"class":15356},[1107,27412,20954],{"class":1779},[1107,27414,27115],{"class":1783},[1107,27416,27417],{"class":1854}," 3.0",[1107,27419,5192],{"class":1779},[1107,27421,27422,27424,27426,27428,27430,27432,27435,27437,27439,27441,27443,27445,27447,27449,27452],{"class":1765,"line":294},[1107,27423,15228],{"class":1779},[1107,27425,29],{"class":1783},[1107,27427,15357],{"class":15356},[1107,27429,15360],{"class":1779},[1107,27431,15363],{"class":1783},[1107,27433,27434],{"class":1966},"isoOutput4",[1107,27436,15363],{"class":1783},[1107,27438,1955],{"class":1783},[1107,27440,17805],{"class":1779},[1107,27442,29],{"class":1783},[1107,27444,27110],{"class":15356},[1107,27446,20954],{"class":1779},[1107,27448,27115],{"class":1783},[1107,27450,27451],{"class":1854}," 4.0",[1107,27453,5192],{"class":1779},[1107,27455,27456,27458,27460,27462,27464,27466,27469,27471,27473,27475,27477,27479,27481,27483,27486],{"class":1765,"line":104},[1107,27457,15228],{"class":1779},[1107,27459,29],{"class":1783},[1107,27461,15357],{"class":15356},[1107,27463,15360],{"class":1779},[1107,27465,15363],{"class":1783},[1107,27467,27468],{"class":1966},"isoOutput5",[1107,27470,15363],{"class":1783},[1107,27472,1955],{"class":1783},[1107,27474,17805],{"class":1779},[1107,27476,29],{"class":1783},[1107,27478,27110],{"class":15356},[1107,27480,20954],{"class":1779},[1107,27482,27115],{"class":1783},[1107,27484,27485],{"class":1854}," 5.0",[1107,27487,5192],{"class":1779},[1107,27489,27490,27492,27494,27496,27498,27500,27503,27505,27507,27509,27511,27513,27515,27517,27520],{"class":1765,"line":1868},[1107,27491,15228],{"class":1779},[1107,27493,29],{"class":1783},[1107,27495,15357],{"class":15356},[1107,27497,15360],{"class":1779},[1107,27499,15363],{"class":1783},[1107,27501,27502],{"class":1966},"isoOutput6",[1107,27504,15363],{"class":1783},[1107,27506,1955],{"class":1783},[1107,27508,17805],{"class":1779},[1107,27510,29],{"class":1783},[1107,27512,27110],{"class":15356},[1107,27514,20954],{"class":1779},[1107,27516,27115],{"class":1783},[1107,27518,27519],{"class":1854}," 6.0",[1107,27521,5192],{"class":1779},[1107,27523,27524,27526,27528,27530,27532,27534,27537,27539,27541,27543,27545,27547,27549,27551,27554],{"class":1765,"line":1896},[1107,27525,15228],{"class":1779},[1107,27527,29],{"class":1783},[1107,27529,15357],{"class":15356},[1107,27531,15360],{"class":1779},[1107,27533,15363],{"class":1783},[1107,27535,27536],{"class":1966},"isoOutput7",[1107,27538,15363],{"class":1783},[1107,27540,1955],{"class":1783},[1107,27542,17805],{"class":1779},[1107,27544,29],{"class":1783},[1107,27546,27110],{"class":15356},[1107,27548,20954],{"class":1779},[1107,27550,27115],{"class":1783},[1107,27552,27553],{"class":1854}," 7.0",[1107,27555,5192],{"class":1779},[1107,27557,27558,27560,27562,27564,27566,27568,27570,27572,27574,27576,27578,27580,27582,27584,27587],{"class":1765,"line":1623},[1107,27559,15228],{"class":1779},[1107,27561,29],{"class":1783},[1107,27563,15357],{"class":15356},[1107,27565,15360],{"class":1779},[1107,27567,15363],{"class":1783},[1107,27569,27347],{"class":1966},[1107,27571,15363],{"class":1783},[1107,27573,1955],{"class":1783},[1107,27575,17805],{"class":1779},[1107,27577,29],{"class":1783},[1107,27579,27110],{"class":15356},[1107,27581,20954],{"class":1779},[1107,27583,27115],{"class":1783},[1107,27585,27586],{"class":1854}," 8.0",[1107,27588,5192],{"class":1779},[1107,27590,27591],{"class":1765,"line":1924},[1107,27592,1921],{"emptyLinePlaceholder":129},[1107,27594,27595],{"class":1765,"line":1930},[1107,27596,27332],{"class":1783},[15,27598,27599],{},"We can confirm the values are being stored in memory by checking the flow context data and pressing the refresh button.",[15,27601,27602],{},[165,27603],{"alt":27604,"src":27605,"title":27606},"\"Screenshot showing the Context Data option\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fcontext-data-1.png","Screenshot showing the Context Data option",[15,27608,27609,27613],{},[165,27610],{"alt":27611,"src":27612,"title":27611},"Screenshot showing the flow variables in the context data tab","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fcontext-data-2.png","\nEach time we hit refresh, the values change, confirming that the values are randomly changing every second.",[15,27615,27616,27617,27620],{},"The last, and most important part of the flow, is the ",[924,27618,27619],{},"Compact-Server"," node, which actually stands alone without any incoming or outgoing connections.",[15,27622,27623,25063,27628,27630,27631,27634,27635,1413,27637,27640,27641,27643],{},[165,27624],{"alt":27625,"src":27626,"title":27627},"\"Compact Server Node\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fcompact-server-node.png","Compact Server Node",[924,27629,27619],{}," node properties, the first tab is ",[924,27632,27633],{},"Settings",", and the two important properties here are ",[924,27636,14666],{},[924,27638,27639],{},"Show Errors",".  As can be seen in the node screenshot above, the node is reporting ",[924,27642,15406],{},", which means the server is configured correctly.",[15,27645,27646],{},[165,27647],{"alt":27648,"src":27649,"title":27648},"Screenshot showing the Settings Tab of compact server node","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fsettings-tab.png",[15,27651,175,27652,27655],{},[924,27653,27654],{},"Limits"," tab specifies some default limits that we can configure if we like, but are not necessary to be modified for test purposes.",[15,27657,175,27658,27660,27661,27664],{},[924,27659,10371],{}," tab has one important option, ",[924,27662,27663],{},"Allow Anonymous",".  By default, anonymous access is enabled.",[15,27666,27667,27671],{},[165,27668],{"alt":27669,"src":27670,"title":27669},"Screenshot showing the Security Tab of compact server node","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fsecurity-tab.png","\nFor a production system, we will want to enable security, but for test purposes, we will leave anonymous access enabled.",[15,27673,27674,27677],{},[924,27675,27676],{},"Users & Sets"," tab is related to security and permissions.  We can leave this empty for testing.",[15,27679,175,27680,27682,27683,29],{},[924,27681,24327],{}," tab is where our server OPC Information Model is constructed, using classes and methods from the ",[63,27684,27687],{"href":27685,"rel":27686},"https:\u002F\u002Fnode-opcua.github.io\u002F",[67],"node-opcua sdk",[15,27689,27690],{},"Breaking down the provided example code for further context, it starts with a function that is responsible for invoking the OPC-UA server,",[1756,27692,27694],{"className":1758,"code":27693,"language":1760,"meta":103,"style":103},"  const opcua = coreServer.choreCompact.opcua;\n",[924,27695,27696],{"__ignoreMap":103},[1107,27697,27698,27701,27704,27706,27709,27711,27714,27716,27718],{"class":1765,"line":1766},[1107,27699,27700],{"class":1775},"  const",[1107,27702,27703],{"class":1779}," opcua ",[1107,27705,1784],{"class":1783},[1107,27707,27708],{"class":1779}," coreServer",[1107,27710,29],{"class":1783},[1107,27712,27713],{"class":1779},"choreCompact",[1107,27715,29],{"class":1783},[1107,27717,11799],{"class":1779},[1107,27719,1805],{"class":1783},[15,27721,27722],{},"and then the namespace is created.",[1756,27724,27726],{"className":1758,"code":27725,"language":1760,"meta":103,"style":103},"const namespace = addressSpace.getOwnNamespace();\n",[924,27727,27728],{"__ignoreMap":103},[1107,27729,27730,27732,27735,27737,27740,27742,27745,27747],{"class":1765,"line":1766},[1107,27731,1776],{"class":1775},[1107,27733,27734],{"class":1779}," namespace ",[1107,27736,1784],{"class":1783},[1107,27738,27739],{"class":1779}," addressSpace",[1107,27741,29],{"class":1783},[1107,27743,27744],{"class":15356},"getOwnNamespace",[1107,27746,17639],{"class":1779},[1107,27748,1805],{"class":1783},[15,27750,27751,27752,27755,27756,27759],{},"Further down, the variables that will be published by the server (which are our ",[924,27753,27754],{},"isoInput"," & ",[924,27757,27758],{},"isoOutput"," flow context variables) are initialized,",[1756,27761,27763],{"className":1758,"code":27762,"language":1760,"meta":103,"style":103},"  this.sandboxFlowContext.set(\"isoInput1\", 0);\n  this.setInterval(() => {\n    flexServerInternals.sandboxFlowContext.set(\n      \"isoInput1\",\n      Math.random() + 50.0\n    );\n  }, 500);\n  this.sandboxFlowContext.set(\"isoInput2\", 0);\n  this.sandboxFlowContext.set(\"isoInput3\", 0);\n...\n",[924,27764,27765,27794,27809,27825,27836,27852,27859,27871,27897,27923],{"__ignoreMap":103},[1107,27766,27767,27770,27773,27775,27777,27779,27781,27784,27786,27788,27790,27792],{"class":1765,"line":1766},[1107,27768,27769],{"class":1783},"  this.",[1107,27771,27772],{"class":1779},"sandboxFlowContext",[1107,27774,29],{"class":1783},[1107,27776,15357],{"class":15356},[1107,27778,15360],{"class":1779},[1107,27780,1970],{"class":1783},[1107,27782,27783],{"class":1966},"isoInput1",[1107,27785,1970],{"class":1783},[1107,27787,1955],{"class":1783},[1107,27789,15738],{"class":1854},[1107,27791,8879],{"class":1779},[1107,27793,1805],{"class":1783},[1107,27795,27796,27798,27801,27803,27805,27807],{"class":1765,"line":107},[1107,27797,27769],{"class":1783},[1107,27799,27800],{"class":15356},"setInterval",[1107,27802,15360],{"class":1779},[1107,27804,17639],{"class":1783},[1107,27806,16966],{"class":1775},[1107,27808,10797],{"class":1783},[1107,27810,27811,27814,27816,27818,27820,27822],{"class":1765,"line":294},[1107,27812,27813],{"class":1779},"    flexServerInternals",[1107,27815,29],{"class":1783},[1107,27817,27772],{"class":1779},[1107,27819,29],{"class":1783},[1107,27821,15357],{"class":15356},[1107,27823,27824],{"class":1946},"(\n",[1107,27826,27827,27830,27832,27834],{"class":1765,"line":104},[1107,27828,27829],{"class":1783},"      \"",[1107,27831,27783],{"class":1966},[1107,27833,1970],{"class":1783},[1107,27835,9079],{"class":1783},[1107,27837,27838,27841,27843,27845,27847,27849],{"class":1765,"line":1868},[1107,27839,27840],{"class":1779},"      Math",[1107,27842,29],{"class":1783},[1107,27844,27110],{"class":15356},[1107,27846,20954],{"class":1946},[1107,27848,27115],{"class":1783},[1107,27850,27851],{"class":1854}," 50.0\n",[1107,27853,27854,27857],{"class":1765,"line":1896},[1107,27855,27856],{"class":1946},"    )",[1107,27858,1805],{"class":1783},[1107,27860,27861,27864,27867,27869],{"class":1765,"line":1623},[1107,27862,27863],{"class":1783},"  },",[1107,27865,27866],{"class":1854}," 500",[1107,27868,8879],{"class":1779},[1107,27870,1805],{"class":1783},[1107,27872,27873,27875,27877,27879,27881,27883,27885,27887,27889,27891,27893,27895],{"class":1765,"line":1924},[1107,27874,27769],{"class":1783},[1107,27876,27772],{"class":1779},[1107,27878,29],{"class":1783},[1107,27880,15357],{"class":15356},[1107,27882,15360],{"class":1779},[1107,27884,1970],{"class":1783},[1107,27886,27075],{"class":1966},[1107,27888,1970],{"class":1783},[1107,27890,1955],{"class":1783},[1107,27892,15738],{"class":1854},[1107,27894,8879],{"class":1779},[1107,27896,1805],{"class":1783},[1107,27898,27899,27901,27903,27905,27907,27909,27911,27913,27915,27917,27919,27921],{"class":1765,"line":1930},[1107,27900,27769],{"class":1783},[1107,27902,27772],{"class":1779},[1107,27904,29],{"class":1783},[1107,27906,15357],{"class":15356},[1107,27908,15360],{"class":1779},[1107,27910,1970],{"class":1783},[1107,27912,27135],{"class":1966},[1107,27914,1970],{"class":1783},[1107,27916,1955],{"class":1783},[1107,27918,15738],{"class":1854},[1107,27920,8879],{"class":1779},[1107,27922,1805],{"class":1783},[1107,27924,27925],{"class":1765,"line":1940},[1107,27926,27332],{"class":1783},[15,27928,27929],{},"and an OPC folder structure is defined.",[1756,27931,27933],{"className":1758,"code":27932,"language":1760,"meta":103,"style":103},"  coreServer.debugLog(\"init dynamic address space\");\n  const rootFolder = addressSpace.findNode(\"RootFolder\");\n\n  node.warn(\"construct new address space for OPC UA\");\n\n  const myDevice = namespace.addFolder(rootFolder.objects, {\n    \"browseName\": \"RaspberryPI-Zero-WLAN\"\n  });\n...\n",[924,27934,27935,27958,27987,27991,28014,28018,28047,28064,28073],{"__ignoreMap":103},[1107,27936,27937,27940,27942,27945,27947,27949,27952,27954,27956],{"class":1765,"line":1766},[1107,27938,27939],{"class":1779},"  coreServer",[1107,27941,29],{"class":1783},[1107,27943,27944],{"class":15356},"debugLog",[1107,27946,15360],{"class":1779},[1107,27948,1970],{"class":1783},[1107,27950,27951],{"class":1966},"init dynamic address space",[1107,27953,1970],{"class":1783},[1107,27955,8879],{"class":1779},[1107,27957,1805],{"class":1783},[1107,27959,27960,27962,27965,27967,27969,27971,27974,27976,27978,27981,27983,27985],{"class":1765,"line":107},[1107,27961,27700],{"class":1775},[1107,27963,27964],{"class":1779}," rootFolder ",[1107,27966,1784],{"class":1783},[1107,27968,27739],{"class":1779},[1107,27970,29],{"class":1783},[1107,27972,27973],{"class":15356},"findNode",[1107,27975,15360],{"class":1779},[1107,27977,1970],{"class":1783},[1107,27979,27980],{"class":1966},"RootFolder",[1107,27982,1970],{"class":1783},[1107,27984,8879],{"class":1779},[1107,27986,1805],{"class":1783},[1107,27988,27989],{"class":1765,"line":294},[1107,27990,1921],{"emptyLinePlaceholder":129},[1107,27992,27993,27996,27998,28001,28003,28005,28008,28010,28012],{"class":1765,"line":104},[1107,27994,27995],{"class":1779},"  node",[1107,27997,29],{"class":1783},[1107,27999,28000],{"class":15356},"warn",[1107,28002,15360],{"class":1779},[1107,28004,1970],{"class":1783},[1107,28006,28007],{"class":1966},"construct new address space for OPC UA",[1107,28009,1970],{"class":1783},[1107,28011,8879],{"class":1779},[1107,28013,1805],{"class":1783},[1107,28015,28016],{"class":1765,"line":1868},[1107,28017,1921],{"emptyLinePlaceholder":129},[1107,28019,28020,28022,28025,28027,28030,28032,28035,28038,28040,28043,28045],{"class":1765,"line":1896},[1107,28021,27700],{"class":1775},[1107,28023,28024],{"class":1779}," myDevice ",[1107,28026,1784],{"class":1783},[1107,28028,28029],{"class":1779}," namespace",[1107,28031,29],{"class":1783},[1107,28033,28034],{"class":15356},"addFolder",[1107,28036,28037],{"class":1779},"(rootFolder",[1107,28039,29],{"class":1783},[1107,28041,28042],{"class":1779},"objects",[1107,28044,1955],{"class":1783},[1107,28046,10797],{"class":1783},[1107,28048,28049,28051,28053,28055,28057,28059,28062],{"class":1765,"line":1623},[1107,28050,10521],{"class":1783},[1107,28052,24425],{"class":1946},[1107,28054,1970],{"class":1783},[1107,28056,992],{"class":1783},[1107,28058,1963],{"class":1783},[1107,28060,28061],{"class":1966},"RaspberryPI-Zero-WLAN",[1107,28063,17207],{"class":1783},[1107,28065,28066,28069,28071],{"class":1765,"line":1924},[1107,28067,28068],{"class":1783},"  }",[1107,28070,8879],{"class":1779},[1107,28072,1805],{"class":1783},[1107,28074,28075],{"class":1765,"line":1930},[1107,28076,27332],{"class":1783},[15,28078,28079],{},"Then, with our variables and folder structure defined, nodes are added to the namespace for each context variable.",[1756,28081,28083],{"className":1758,"code":28082,"language":1760,"meta":103,"style":103},"  const gpioDI1 = namespace.addVariable({\n    \"organizedBy\": isoInputs,\n    \"browseName\": \"I1\",\n    \"nodeId\": \"ns=1;s=Isolated_Input1\",\n    \"dataType\": \"Double\",\n    \"value\": {\n      \"get\": function() {\n        return new Variant({\n          \"dataType\": DataType.Double,\n          \"value\": flexServerInternals.sandboxFlowContext.get(\"isoInput1\")\n        });\n      },\n      \"set\": function(variant) {\n        flexServerInternals.sandboxFlowContext.set(\n          \"isoInput1\",\n          parseFloat(variant.value)\n        );\n        return opcua.StatusCodes.Good;\n      }\n    }\n  });\n\n...\n",[924,28084,28085,28105,28121,28140,28159,28179,28191,28208,28222,28242,28273,28282,28287,28308,28323,28333,28348,28355,28373,28377,28381,28389,28393],{"__ignoreMap":103},[1107,28086,28087,28089,28092,28094,28096,28098,28101,28103],{"class":1765,"line":1766},[1107,28088,27700],{"class":1775},[1107,28090,28091],{"class":1779}," gpioDI1 ",[1107,28093,1784],{"class":1783},[1107,28095,28029],{"class":1779},[1107,28097,29],{"class":1783},[1107,28099,28100],{"class":15356},"addVariable",[1107,28102,15360],{"class":1779},[1107,28104,9062],{"class":1783},[1107,28106,28107,28109,28112,28114,28116,28119],{"class":1765,"line":107},[1107,28108,10521],{"class":1783},[1107,28110,28111],{"class":1946},"organizedBy",[1107,28113,1970],{"class":1783},[1107,28115,992],{"class":1783},[1107,28117,28118],{"class":1779}," isoInputs",[1107,28120,9079],{"class":1783},[1107,28122,28123,28125,28127,28129,28131,28133,28136,28138],{"class":1765,"line":294},[1107,28124,10521],{"class":1783},[1107,28126,24425],{"class":1946},[1107,28128,1970],{"class":1783},[1107,28130,992],{"class":1783},[1107,28132,1963],{"class":1783},[1107,28134,28135],{"class":1966},"I1",[1107,28137,1970],{"class":1783},[1107,28139,9079],{"class":1783},[1107,28141,28142,28144,28146,28148,28150,28152,28155,28157],{"class":1765,"line":104},[1107,28143,10521],{"class":1783},[1107,28145,24436],{"class":1946},[1107,28147,1970],{"class":1783},[1107,28149,992],{"class":1783},[1107,28151,1963],{"class":1783},[1107,28153,28154],{"class":1966},"ns=1;s=Isolated_Input1",[1107,28156,1970],{"class":1783},[1107,28158,9079],{"class":1783},[1107,28160,28161,28163,28166,28168,28170,28172,28175,28177],{"class":1765,"line":1868},[1107,28162,10521],{"class":1783},[1107,28164,28165],{"class":1946},"dataType",[1107,28167,1970],{"class":1783},[1107,28169,992],{"class":1783},[1107,28171,1963],{"class":1783},[1107,28173,28174],{"class":1966},"Double",[1107,28176,1970],{"class":1783},[1107,28178,9079],{"class":1783},[1107,28180,28181,28183,28185,28187,28189],{"class":1765,"line":1896},[1107,28182,10521],{"class":1783},[1107,28184,10563],{"class":1946},[1107,28186,1970],{"class":1783},[1107,28188,992],{"class":1783},[1107,28190,10797],{"class":1783},[1107,28192,28193,28195,28197,28199,28201,28204,28206],{"class":1765,"line":1623},[1107,28194,27829],{"class":1783},[1107,28196,15453],{"class":1946},[1107,28198,1970],{"class":1783},[1107,28200,992],{"class":1783},[1107,28202,28203],{"class":1775}," function",[1107,28205,17639],{"class":1783},[1107,28207,10797],{"class":1783},[1107,28209,28210,28213,28215,28218,28220],{"class":1765,"line":1924},[1107,28211,28212],{"class":1933},"        return",[1107,28214,17760],{"class":1783},[1107,28216,28217],{"class":15356}," Variant",[1107,28219,15360],{"class":1946},[1107,28221,9062],{"class":1783},[1107,28223,28224,28227,28229,28231,28233,28236,28238,28240],{"class":1765,"line":1930},[1107,28225,28226],{"class":1783},"          \"",[1107,28228,28165],{"class":1946},[1107,28230,1970],{"class":1783},[1107,28232,992],{"class":1783},[1107,28234,28235],{"class":1779}," DataType",[1107,28237,29],{"class":1783},[1107,28239,28174],{"class":1779},[1107,28241,9079],{"class":1783},[1107,28243,28244,28246,28248,28250,28252,28255,28257,28259,28261,28263,28265,28267,28269,28271],{"class":1765,"line":1940},[1107,28245,28226],{"class":1783},[1107,28247,10563],{"class":1946},[1107,28249,1970],{"class":1783},[1107,28251,992],{"class":1783},[1107,28253,28254],{"class":1779}," flexServerInternals",[1107,28256,29],{"class":1783},[1107,28258,27772],{"class":1779},[1107,28260,29],{"class":1783},[1107,28262,15453],{"class":15356},[1107,28264,15360],{"class":1946},[1107,28266,1970],{"class":1783},[1107,28268,27783],{"class":1966},[1107,28270,1970],{"class":1783},[1107,28272,5192],{"class":1946},[1107,28274,28275,28278,28280],{"class":1765,"line":1976},[1107,28276,28277],{"class":1783},"        }",[1107,28279,8879],{"class":1946},[1107,28281,1805],{"class":1783},[1107,28283,28284],{"class":1765,"line":2002},[1107,28285,28286],{"class":1783},"      },\n",[1107,28288,28289,28291,28293,28295,28297,28299,28301,28304,28306],{"class":1765,"line":2029},[1107,28290,27829],{"class":1783},[1107,28292,15357],{"class":1946},[1107,28294,1970],{"class":1783},[1107,28296,992],{"class":1783},[1107,28298,28203],{"class":1775},[1107,28300,15360],{"class":1783},[1107,28302,28303],{"class":16960},"variant",[1107,28305,8879],{"class":1783},[1107,28307,10797],{"class":1783},[1107,28309,28310,28313,28315,28317,28319,28321],{"class":1765,"line":1691},[1107,28311,28312],{"class":1779},"        flexServerInternals",[1107,28314,29],{"class":1783},[1107,28316,27772],{"class":1779},[1107,28318,29],{"class":1783},[1107,28320,15357],{"class":15356},[1107,28322,27824],{"class":1946},[1107,28324,28325,28327,28329,28331],{"class":1765,"line":2080},[1107,28326,28226],{"class":1783},[1107,28328,27783],{"class":1966},[1107,28330,1970],{"class":1783},[1107,28332,9079],{"class":1783},[1107,28334,28335,28338,28340,28342,28344,28346],{"class":1765,"line":7873},[1107,28336,28337],{"class":15356},"          parseFloat",[1107,28339,15360],{"class":1946},[1107,28341,28303],{"class":1779},[1107,28343,29],{"class":1783},[1107,28345,10563],{"class":1779},[1107,28347,5192],{"class":1946},[1107,28349,28350,28353],{"class":1765,"line":7879},[1107,28351,28352],{"class":1946},"        )",[1107,28354,1805],{"class":1783},[1107,28356,28357,28359,28362,28364,28367,28369,28371],{"class":1765,"line":7885},[1107,28358,28212],{"class":1933},[1107,28360,28361],{"class":1779}," opcua",[1107,28363,29],{"class":1783},[1107,28365,28366],{"class":1779},"StatusCodes",[1107,28368,29],{"class":1783},[1107,28370,12420],{"class":1779},[1107,28372,1805],{"class":1783},[1107,28374,28375],{"class":1765,"line":7891},[1107,28376,21026],{"class":1783},[1107,28378,28379],{"class":1765,"line":7897},[1107,28380,10820],{"class":1783},[1107,28382,28383,28385,28387],{"class":1765,"line":7902},[1107,28384,28068],{"class":1783},[1107,28386,8879],{"class":1779},[1107,28388,1805],{"class":1783},[1107,28390,28391],{"class":1765,"line":7908},[1107,28392,1921],{"emptyLinePlaceholder":129},[1107,28394,28395],{"class":1765,"line":7914},[1107,28396,27332],{"class":1783},[15,28398,28399],{},"Last, OPC views are defined.  Views create custom hierarchies our OPC Client can browse as an alternative to the default folder structure.",[1756,28401,28403],{"className":1758,"code":28402,"language":1760,"meta":103,"style":103},"  const viewDI = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"RPIW0-Digital-Ins\"\n  });\n\n  const viewDO = namespace.addView({\n    \"organizedBy\": rootFolder.views,\n    \"browseName\": \"RPIW0-Digital-Outs\"\n  });\n\n  viewDI.addReference({\n    \"referenceType\": \"Organizes\",\n    \"nodeId\": gpioDI1.nodeId\n  });\n\n...\n\n",[924,28404,28405,28425,28445,28462,28470,28474,28493,28511,28528,28536,28540,28554,28574,28592,28600,28604],{"__ignoreMap":103},[1107,28406,28407,28409,28412,28414,28416,28418,28421,28423],{"class":1765,"line":1766},[1107,28408,27700],{"class":1775},[1107,28410,28411],{"class":1779}," viewDI ",[1107,28413,1784],{"class":1783},[1107,28415,28029],{"class":1779},[1107,28417,29],{"class":1783},[1107,28419,28420],{"class":15356},"addView",[1107,28422,15360],{"class":1779},[1107,28424,9062],{"class":1783},[1107,28426,28427,28429,28431,28433,28435,28438,28440,28443],{"class":1765,"line":107},[1107,28428,10521],{"class":1783},[1107,28430,28111],{"class":1946},[1107,28432,1970],{"class":1783},[1107,28434,992],{"class":1783},[1107,28436,28437],{"class":1779}," rootFolder",[1107,28439,29],{"class":1783},[1107,28441,28442],{"class":1779},"views",[1107,28444,9079],{"class":1783},[1107,28446,28447,28449,28451,28453,28455,28457,28460],{"class":1765,"line":294},[1107,28448,10521],{"class":1783},[1107,28450,24425],{"class":1946},[1107,28452,1970],{"class":1783},[1107,28454,992],{"class":1783},[1107,28456,1963],{"class":1783},[1107,28458,28459],{"class":1966},"RPIW0-Digital-Ins",[1107,28461,17207],{"class":1783},[1107,28463,28464,28466,28468],{"class":1765,"line":104},[1107,28465,28068],{"class":1783},[1107,28467,8879],{"class":1779},[1107,28469,1805],{"class":1783},[1107,28471,28472],{"class":1765,"line":1868},[1107,28473,1921],{"emptyLinePlaceholder":129},[1107,28475,28476,28478,28481,28483,28485,28487,28489,28491],{"class":1765,"line":1896},[1107,28477,27700],{"class":1775},[1107,28479,28480],{"class":1779}," viewDO ",[1107,28482,1784],{"class":1783},[1107,28484,28029],{"class":1779},[1107,28486,29],{"class":1783},[1107,28488,28420],{"class":15356},[1107,28490,15360],{"class":1779},[1107,28492,9062],{"class":1783},[1107,28494,28495,28497,28499,28501,28503,28505,28507,28509],{"class":1765,"line":1623},[1107,28496,10521],{"class":1783},[1107,28498,28111],{"class":1946},[1107,28500,1970],{"class":1783},[1107,28502,992],{"class":1783},[1107,28504,28437],{"class":1779},[1107,28506,29],{"class":1783},[1107,28508,28442],{"class":1779},[1107,28510,9079],{"class":1783},[1107,28512,28513,28515,28517,28519,28521,28523,28526],{"class":1765,"line":1924},[1107,28514,10521],{"class":1783},[1107,28516,24425],{"class":1946},[1107,28518,1970],{"class":1783},[1107,28520,992],{"class":1783},[1107,28522,1963],{"class":1783},[1107,28524,28525],{"class":1966},"RPIW0-Digital-Outs",[1107,28527,17207],{"class":1783},[1107,28529,28530,28532,28534],{"class":1765,"line":1930},[1107,28531,28068],{"class":1783},[1107,28533,8879],{"class":1779},[1107,28535,1805],{"class":1783},[1107,28537,28538],{"class":1765,"line":1940},[1107,28539,1921],{"emptyLinePlaceholder":129},[1107,28541,28542,28545,28547,28550,28552],{"class":1765,"line":1976},[1107,28543,28544],{"class":1779},"  viewDI",[1107,28546,29],{"class":1783},[1107,28548,28549],{"class":15356},"addReference",[1107,28551,15360],{"class":1779},[1107,28553,9062],{"class":1783},[1107,28555,28556,28558,28561,28563,28565,28567,28570,28572],{"class":1765,"line":2002},[1107,28557,10521],{"class":1783},[1107,28559,28560],{"class":1946},"referenceType",[1107,28562,1970],{"class":1783},[1107,28564,992],{"class":1783},[1107,28566,1963],{"class":1783},[1107,28568,28569],{"class":1966},"Organizes",[1107,28571,1970],{"class":1783},[1107,28573,9079],{"class":1783},[1107,28575,28576,28578,28580,28582,28584,28587,28589],{"class":1765,"line":2029},[1107,28577,10521],{"class":1783},[1107,28579,24436],{"class":1946},[1107,28581,1970],{"class":1783},[1107,28583,992],{"class":1783},[1107,28585,28586],{"class":1779}," gpioDI1",[1107,28588,29],{"class":1783},[1107,28590,28591],{"class":1779},"nodeId\n",[1107,28593,28594,28596,28598],{"class":1765,"line":1691},[1107,28595,28068],{"class":1783},[1107,28597,8879],{"class":1779},[1107,28599,1805],{"class":1783},[1107,28601,28602],{"class":1765,"line":2080},[1107,28603,1921],{"emptyLinePlaceholder":129},[1107,28605,28606],{"class":1765,"line":7873},[1107,28607,27332],{"class":1783},[15,28609,28610,28611,28614],{},"Finally, on the ",[924,28612,28613],{},"Discovery"," tab, we must define an endpoint for an OPC Client to subscribe to.",[15,28616,175,28617,28620,28621,28624,28625,28627,28628,28631,28632],{},[924,28618,28619],{},"Endpoint Url"," follows the format ",[924,28622,28623],{},"opc.tcp:\u002F\u002F\u003Caddress>:port",".  Our port was defined on the ",[924,28626,27633],{}," tab, which by default, is port ",[924,28629,28630],{},"54845",". The address will be either the url or ip address of your Node-RED instance.  In my case, it’s 192.168.0.114.  So my Endpoint Url = ",[924,28633,28634],{},"opc.tcp:\u002F\u002F192.168.0.114:54845",[15,28636,28637,28641],{},[165,28638],{"alt":28639,"src":28640,"title":28639},"Screenshot showing the Discovery Tab of compact server node","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fdiscovery-tab.png","\nOnce the endpoint url is added, deploy the flow, and confirm the server is reporting “active”.",[15,28643,28644],{},[165,28645],{"alt":28646,"src":28647,"title":28646},"Screenshot showing the Active Tab of compact server node","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fcompact-server-active.png",[31,28649,28651],{"id":28650},"connect-to-example-opc-server-using-opc-ua-browser","Connect to Example OPC-Server Using OPC-UA Browser",[15,28653,28654,28655,28660],{},"To connect to our OPC endpoint, we need an OPC Client.  Prosys provides a ",[63,28656,28659],{"href":28657,"rel":28658},"https:\u002F\u002Fwww.prosysopc.com\u002Fproducts\u002Fopc-ua-browser\u002F",[67],"free OPC-UA Browser ","that supports Windows, Linux, and Mac OS.  To test our Server, the Windows version of Prosys OPC-UA Browser will be utilized.",[15,28662,28663],{},"To connect to our Node-RED OPC server, enter the endpoint url and press “connect to server”.",[15,28665,28666,28671,28672,28675],{},[165,28667],{"alt":28668,"src":28669,"title":28670},"\"Screenshot showing the OPC Client\"","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-client-connect.png","Screenshot showing the OPC Client","\nIt will ask for security.  Remember that we allowed anonymous access, so the default security mode of ",[924,28673,28674],{},"None"," is the correct option.",[15,28677,28678],{},"Once connected, we can browse our OPC Server.",[15,28680,28681,28685,28686,28689,28690,28692],{},[165,28682],{"alt":28683,"src":28684},"OPC Client UI","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-client-ui.png","\nIf we navigate to ",[924,28687,28688],{},"Objects → RaspberryPI-Zero-WLAN → GPIO → Inputs",", we can see a list of inputs that correspond to the ",[924,28691,27754],{}," context variables defined in the example flow, which are randomly generated numbers.",[15,28694,28695,28696,28698],{},"Clicking ",[924,28697,28135],{}," we can see the value in real-time, along with some additional properties.",[15,28700,28701,28705,28706,28709],{},[165,28702],{"alt":28703,"src":28704,"title":28703},"OPC Client Node","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-client-node.png","\nIf we go to ",[924,28707,28708],{},"Views",", we can see the custom hierarchy defined in the example server, which divides the data by Digital-Ins and Digital-Outs.",[15,28711,28712],{},[165,28713],{"alt":28714,"src":28715},"OPC Client View","\u002Fblog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-client-view.png",[31,28717,18485],{"id":18484},[15,28719,28720],{},"In this article, we compare OPC-UA to traditional fieldbus protocols, explain the importance of the OPC UA Information Model to understand how data is modeled in the address space of an OPC Server, and then walk through and deploy an example compact OPC-UA Server flow.",[15,28722,28723],{},"In our next article, we will build a custom OPC-UA Server in Node-RED with data pulled from an Allen Bradley PLC over Ethernet\u002FIP, using the PLC data to develop a custom OPC UA Information Model programmed in the OPC server address space.",[2582,28725,28726],{},"html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":103,"searchDepth":104,"depth":104,"links":28728},[28729,28730,28731,28732,28733],{"id":26920,"depth":107,"text":26921},{"id":26945,"depth":107,"text":26946},{"id":27031,"depth":107,"text":27032},{"id":28650,"depth":107,"text":28651},{"id":18484,"depth":107,"text":18485},{"type":748,"title":28735,"description":28736},"Run Your OPC UA Node-RED Server in the Cloud","Sign up for FlowFuse to deploy, manage, and scale your Node-RED OPC UA server flows securely in the cloud with built-in DevOps pipelines.","2023-07-13","Introduction to OPC-UA and how to deploy a Node-RED server flow.","blog\u002F2023\u002F07\u002Fimages\u002Fopc-ua-1\u002Fopc-ua-1-title-image.png",{"keywords":28741,"excerpt":28742},"opc ua server, opc ua server free, opc ua gateway, opc ua example, node-red-contrib-opcua, nodered opcua, node red opcua, node-red opcua, opcua node red, opcua nodered",{"type":12,"value":28743},[28744],[15,28745,26917],{},"\u002Fblog\u002F2023\u002F07\u002Fhow-to-deploy-a-basic-opc-ua-server-in-node-red",{"title":26911,"description":28738},{"loc":28746},"blog\u002F2023\u002F07\u002Fhow-to-deploy-a-basic-opc-ua-server-in-node-red","OPC-UA Server Information Modeling in Node-RED",[8650,138,11799,7457],"This first installment in an OPC UA series explains the OPC UA information model how devices are represented as structured node objects rather than raw register values and walks through deploying a working OPC UA server flow in Node-RED using the node-red-contrib-opcua package. Understanding the information model and address-space hierarchy is the key prerequisite before building OPC UA clients or integrations.","aqJxabkyaGvGNG0wnFPvtB40UOJhS_j2ZBJQHM1dw2U",{"id":28755,"title":28756,"authors":28757,"body":28758,"cta":3,"date":28918,"description":28919,"extension":116,"image":28920,"lastUpdated":3,"meta":28921,"navigation":129,"path":28928,"seo":28929,"sitemap":28930,"stem":28931,"subtitle":28932,"tags":28933,"tldr":3,"video":3,"__hash__":28935},"blog\u002Fblog\u002F2023\u002F05\u002Fbringing-high-availability-to-node-red.md","Bringing High Availability to Node-RED",[3731],{"type":12,"value":28759,"toc":28911},[28760,28768,28771,28785,28788,28792,28802,28805,28808,28811,28814,28817,28820,28823,28827,28830,28833,28836,28839,28842,28845,28848,28851,28854,28858,28861,28864,28867,28871,28874,28877,28880,28883,28886,28890,28893,28896,28899,28908],[15,28761,28762,28763,28767],{},"Many companies look to deploy Node-RED into use cases that require the application\nto have a high degree of availability, reliability, and scalability. Following up\nour ",[63,28764,28766],{"href":28765},"\u002Fblog\u002F2023\u002F02\u002Fhighly-available-node-red\u002F","previous post on the subject",", in this\npost I’m going to look at some of the technical details of achieving HA, the\napproaches available and what that means for the work we’re doing at FlowFuse\nand upstream in Node-RED.",[15,28769,28770],{},"Everyone we speak to has a different set of requirements for this topic. To help\nwith the discussion, I’m going to look at two ways of approaching it:",[39,28772,28773,28779],{},[42,28774,175,28775,28778],{},[19,28776,28777],{},"hot-spare approach"," where you have a second instance of the application\nready to take over when the primary fails. This achieves availability but\ndoesn’t contribute to scalability.",[42,28780,175,28781,28784],{},[19,28782,28783],{},"load-balanced approach"," where you have a second active instance of the\napplication and work is shared between them. If either fails, the other\ncontinues running. A side-effect of this approach is a higher potential\nthrough-put and scalability; although in practice you need to ensure capacity\nto tolerate an instance failing.",[15,28786,28787],{},"To consider which approach is most appropriate in the context of Node-RED, we\nneed to look at the benefits and complications of each approach. It comes down\nto two factors; statefulness and how work is routed.",[399,28789,28791],{"id":28790},"statefulness","Statefulness",[15,28793,28794,28795,1413,28798,28801],{},"There are two types of state to consider when thinking about a Node-RED flow:\n",[19,28796,28797],{},"explicit",[19,28799,28800],{},"implicit"," state.",[15,28803,28804],{},"Explicit state is what is programmed into the flow. For example, a flow may store\nstate in Context or use an external database service. Within FlowFuse we provide\ntwo types of context - the default in-memory context store and a database-backed\npersistent store. Currently the database-backed store includes a memory-caching\nlayer to provide better performance and interoperability. That gets tricky when\nyou want to have multiple instances sharing the same store. The context API\ndoesn’t provide a way to atomically update values - so you can get into classic\nconcurrency issues around two applications trying to update the same value.",[15,28806,28807],{},"The other type of state is that which is implicitly maintained in a flow - even\nif the user hasn’t explicitly configured it. For example, the Smooth node can be\nused to calculate a running average value of messages passing through it. The\nnode does that by keeping in memory the recent values so it can recalculate the\naverage with each update. If you have multiple instances, then the node will be\ncalculating the average for just the message its instances sees.",[15,28809,28810],{},"Another example of implicit state is the Batch node that can be used to group\nmessages into batches. Again - it will only be able to do that for the selection\nof messages the instance receives.",[15,28812,28813],{},"It very much depends on the requirements of a flow and what nodes it uses, as to\nhow the state can be handled.",[15,28815,28816],{},"In the hot-spare approach, as only one instance is active at any time, a lot of\nthe explicit state handling will work as expected. However the implicit state\nremains bound to the individual Node-RED instances.",[15,28818,28819],{},"In the load-balanced approach, care has to be taken to ensure any state generated\nby the flow is done in a way that copes with multiple instances accessing it at\nthe same time.",[15,28821,28822],{},"A key take-away from this being that a flow has to be created with HA and\u002For scaling\nin mind.",[399,28824,28826],{"id":28825},"routing-work","Routing work",[15,28828,28829],{},"Node-RED makes it easy to integrate with lots of different sources of events.\nA couple of the most common being HTTP and MQTT. When considering how to handle\nmultiple instances of an application we need to think about how work is routed\nto those instances.",[15,28831,28832],{},"HTTP is the most well understood; you put a load-balancing proxy in front of the\nNode-RED instances and it takes care of sharing out the incoming requests. In\nthe hot-spare scenario, the proxy needs to know which instance is active - that\nrequires some coordination within the platform to track that properly.",[15,28834,28835],{},"MQTT is commonly used with Node-RED, but unlike HTTP which is in-bound, MQTT\nworks by having Node-RED create an out-bound connection to a broker and then\nsubscribing to the topics of interest. In the early days of MQTT that would mean\neach instance would subscribe to the same set of topics and receive every message.\nThat doesn’t really fit any HA model.",[15,28837,28838],{},"With the publication of MQTTv5, the concept of Shared Subscriptions was added;\nthe ability for a group of clients to connect, subscribe to the same topic and\nhave the broker distribute messages between them. At this point you do get load\nbalancing across your Node-RED instances - as long as the MQTT nodes are suitably\nconfigured.",[15,28840,28841],{},"There are lots of other nodes that can be used to trigger flows, whether by\nlistening for events on an API, connecting to locally attached hardware and many\nthings in between. Typically, those that are more cloud-aligned, such as messaging\nsystems like Kafka and AMQP will have very well established ways of doing load\nbalancing.",[15,28843,28844],{},"Managing out-bound connections gets more complicated in the hot-spare scenario.",[15,28846,28847],{},"If we only had to deal with in-bound connections, the hot-spare instance can just\nsit there waiting for work to be passed its way. But once you have out-bound\nconnections, then you have a problem. The hot-spare instance should only create\nits out-bound connections when it becomes the active instance. In real terms,\nthat means the Node-RED flows should only be started when the instance becomes\nactive.",[15,28849,28850],{},"With our goal to minimize the Mean Time To Recovery (MTTR), we need to find a\nway to get that spare instance running as quickly as possible; if it takes just\nas long to start the spare instance as it does to restart the failed primary\ninstance, then it isn’t much of an improvement.",[15,28852,28853],{},"The key here is that Node-RED allows you to start the runtime without the flows\nrunning. That gets everything loaded and the runtime ready ahead of time. It can\nthen start the flows at a moment's notice with a simple call to the runtime admin\nAPI.",[399,28855,28857],{"id":28856},"detecting-failure","Detecting failure",[15,28859,28860],{},"A key requirement of the hot-spare approach to HA is knowing when to failover to\nthe spare.",[15,28862,28863],{},"This requires close monitoring of the active instance to know whether it's still\nworking. How quickly you can detect failure is key to reducing the time to recovery.\nThis is where you have to think about the different ways an instance could fail -\nhas it crashed, has it hung, has it got ‘stuck’?",[15,28865,28866],{},"Detecting failure usually involves some combination of heartbeat ‘pings’ between\nthe instances to check each is able to respond to requests. The spare instance\nthen needs to be able to decide for itself whether it should become the active\ninstance - and do so safely. You do not want to accidentally have two instances\nactive at the same time. This can get quite complicated to achieve safely, but\nthere are a number of approaches that can be used. We’ll be exploring them as we\ncontinue our journey towards HA.",[399,28868,28870],{"id":28869},"editing-flows","Editing Flows",[15,28872,28873],{},"Within the Node-RED architecture, each instance also serves up its own editor.\nThis is what you get when you point your web browser at it.",[15,28875,28876],{},"In a HA world, once you have multiple instances running behind an HTTP load\nbalancer, there is a tricky question of how you edit the flows. If each request\nhits a different instance, just loading the editor will result in different bits\ncoming from different instances. That can typically be solved at the load balancer\nlevel by creating sticky-sessions; ensuring for a given client, each request is\nrouted to a consistent instance. That solves part of the issue, but the next\nchallenge is what to do when the Deploy button is pressed. That is how new flows\nare passed from the editor to the runtime. When you have multiple instances, we\nneed to make sure that they all get updated. That is quite a tricky problem to\nsolve with the current Node-RED APIs - and something we’ll be working on both in\nFlowFuse and in the upstream Node-RED project to resolve.",[15,28878,28879],{},"That said, a more immediate solution could well be to take advantage of separate\ndevelopment\u002Fproduction instances. You develop in a single instance and, when happy\nwith what you’ve got, roll it out to your HA-ready production instance. This\nbypasses the need to edit the flows in the HA environment at all.",[15,28881,28882],{},"Whichever method is used, there is a question of how you minimize downtime whilst\ndeploying an update. In a purely in-bound environment, solutions can be built\nwhere the new application is deployed alongside the old version and, when everything\nis ready, the in-bound events are redirected to the new version. But that isn’t\nfeasible when you have out-bound connections to deal with as well. For some users,\nhaving a scheduled maintenance window for doing updates will be completely acceptable.",[15,28884,28885],{},"As with the hot-spare approach to failover, a similar method could be used that\nstarts new instances of Node-RED alongside the old, but with the flows all stopped.\nThen, once everything is ready, the old instances are stopped and the new instances\nstarted - minimizing the downtime, although not completely removing it.",[399,28887,28889],{"id":28888},"continuing-the-ha-journey-at-flowfuse","Continuing the HA journey at FlowFuse",[15,28891,28892],{},"So the question is how are we going to apply all of this to what we’re building\nat FlowFuse. We cannot do everything at once, so we have to prioritize which\nscenarios we’re going to address first. Consequently, drawing from customer\nfeedback, we have chosen to start with the scaling side of high availability -\nallowing multiple copies of an instance to be run with appropriate load\nbalancing put in front of it.",[15,28894,28895],{},"We are building FlowFuse as an open platform with the ability to run on top of\nDocker Compose and Kubernetes. As we get into some of these HA features, we will\nneed to look carefully at where we can lean on these underlying technologies -\nwe don’t want to reinvent the wheel here.",[15,28897,28898],{},"Our initial focus is going to be when running in a Kubernetes environment - just\nas we do with our hosted FlowFuse Cloud platform. Kubernetes provides lots of\nthe building blocks for creating a scalable and highly available solution, but\nit certainly doesn’t do all of the work for you.",[15,28900,28901,28902,28907],{},"We've identified our initial set of tasks and changes to how we'll run Node-RED\ninstance with the k8s environment. You can follow our progress with this\n",[63,28903,28906],{"href":28904,"rel":28905},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fissues\u002F2156",[67],"issue"," on our backlog.",[15,28909,28910],{},"I hope this post has given some useful insight into the problems we’re looking\nto solve at FlowFuse. As it's such an important requirement for many users we’ll\nkeep you updated as we make progress.",{"title":103,"searchDepth":104,"depth":104,"links":28912},[28913,28914,28915,28916,28917],{"id":28790,"depth":294,"text":28791},{"id":28825,"depth":294,"text":28826},{"id":28856,"depth":294,"text":28857},{"id":28869,"depth":294,"text":28870},{"id":28888,"depth":294,"text":28889},"2023-06-02","Explore how FlowFuse tackles HA challenges, statefulness, and work routing, revolutionizing Node-RED's reliability.","\u002Fimages\u002Fblog\u002Ftile-ha-nr.jpg",{"excerpt":28922},{"type":12,"value":28923},[28924],[15,28925,28762,28926,28767],{},[63,28927,28766],{"href":28765},"\u002Fblog\u002F2023\u002F05\u002Fbringing-high-availability-to-node-red",{"title":28756,"description":28919},{"loc":28928},"blog\u002F2023\u002F05\u002Fbringing-high-availability-to-node-red","How we are tackling the hard problems of HA in FlowFuse",[8650,138,28934],"high availablity","pTDAgqXARBc1VjNa0jM1qlQaiXdr59DHd8lZdUmB3qE",{"id":28937,"title":28938,"authors":28939,"body":28940,"cta":3,"date":29181,"description":29182,"extension":116,"image":29183,"lastUpdated":3,"meta":29184,"navigation":129,"path":29189,"seo":29190,"sitemap":29191,"stem":29192,"subtitle":29193,"tags":29194,"tldr":3,"video":3,"__hash__":29195},"blog\u002Fblog\u002F2023\u002F06\u002F3-quick-node-red-tips-7.md","Node-RED Tips - Dashboard Edition",[19494],{"type":12,"value":28941,"toc":29176},[28942,28945,28952,28956,28959,28962,28965,28968,28974,28977,29044,29048,29051,29057,29060,29063,29086,29090,29093,29096,29102,29105,29108,29133,29140],[15,28943,28944],{},"There is usually more than one way to complete a given task in software, and Node-RED is no exception. In each of this series of blog posts, we are going to share three useful tips to save yourself time when working on your flows.",[15,28946,28947,28948,28951],{},"In this Node-RED Tips article, we are going to focus on ",[63,28949,11862],{"href":11860,"rel":28950},[67],". Dashboard is a great tool for creating HMI (Human Machine Interfaces), it's also the most popular custom node for Node-RED with thousands of downloads per week.",[399,28953,28955],{"id":28954},"_1-responsive-layouts-almost","1. Responsive layouts (almost)",[15,28957,28958],{},"Responsive design is the ability for a webpage to change its content to best fit the features of a device used to view the page. For example, when viewing a graph on a mobile phone or a laptop the available screen space differs significantly in size as well as aspect-ratio.",[15,28960,28961],{},"Dashboard doesn't offer the feature to change graph sizes based on the screen of a viewing device. That being said, there is one trick you can use to make your dashboards a lot more useful on small and large screens alike.",[15,28963,28964],{},"Place your content into Dashboard 'groups', those groups can make use of wider screens by sitting side by side where the screen is big enough while stacking vertically on smaller devices.",[15,28966,28967],{},"The image below shows what happens when you change the screen size for this dashboard.",[15,28969,28970],{},[165,28971],{"alt":28972,"src":28973,"title":28972},"Changing the aspect ratio of the screen","\u002Fblog\u002F2023\u002F06\u002Fimages\u002Fresponsive.gif",[15,28975,28976],{},"If you'd like to try this out on your own Node-RED, you can import the flow below.",[15,28978,28979,28980,1150],{},"{% renderFlow 500 %}\n",[1107,28981,28982,28983,28985,28986,28988,28989,28992,28993,28995,28996,28998,28999,29001,29002,28988,29004,29006,29007,29009,29010,29012,29013,29015,29016,28988,29018,29020,29021,29023,29024,29026,29027,29029,29030,28988,29032,29034,29035,29037,29038,29040,29041,29043],{},"{\"id\":\"e351b1251dfbc2f7\",\"type\":\"tab\",\"label\":\"Flow 1\",\"disabled\":false,\"info\":\"\",\"env\":",[1107,28984],{},"},{\"id\":\"d388b48fcbed93a1\",\"type\":\"ui_gauge\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"ba1ff527abfa5261\",\"order\":2,\"width\":0,\"height\":0,\"gtype\":\"gage\",\"title\":\"gauge\",\"label\":\"units\",\"format\":\"",[2526,28987],{"value":10563},"\",\"min\":0,\"max\":\"100\",\"colors\":",[1107,28990,28991],{},"\"#00b500\",\"#e6e600\",\"#ca3838\"",",\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":80,\"wires\":",[1107,28994],{},"},{\"id\":\"80780894450cfb6d\",\"type\":\"ui_button\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"ba1ff527abfa5261\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":false,\"label\":\"Update\",\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":80,\"y\":80,\"wires\":[[\"49e78ff51c3a1ea3\"]]},{\"id\":\"07b44990e5d5b5f6\",\"type\":\"ui_chart\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"ba1ff527abfa5261\",\"order\":3,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"60\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,28997,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":120,\"wires\":[[]]},{\"id\":\"1482bcf69325aa92\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":",[1107,29000],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":90,\"y\":120,\"wires\":[[\"49e78ff51c3a1ea3\"]]},{\"id\":\"49e78ff51c3a1ea3\",\"type\":\"random\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"low\":\"0\",\"high\":\"100\",\"inte\":\"true\",\"property\":\"payload\",\"x\":220,\"y\":100,\"wires\":[[\"d388b48fcbed93a1\",\"07b44990e5d5b5f6\"]]},{\"id\":\"38996d8eb9f6535d\",\"type\":\"ui_gauge\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"f6052a3dccc77ea3\",\"order\":2,\"width\":0,\"height\":0,\"gtype\":\"gage\",\"title\":\"gauge\",\"label\":\"units\",\"format\":\"",[2526,29003],{"value":10563},[1107,29005,28991],{},",\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":240,\"wires\":",[1107,29008],{},"},{\"id\":\"cf5599fbda28e614\",\"type\":\"ui_button\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"f6052a3dccc77ea3\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":false,\"label\":\"Update\",\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":80,\"y\":240,\"wires\":[[\"f093c63e17a620ba\"]]},{\"id\":\"936438ebf9eef986\",\"type\":\"ui_chart\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"f6052a3dccc77ea3\",\"order\":3,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"60\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29011,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":280,\"wires\":[[]]},{\"id\":\"60a40faa335f943e\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":",[1107,29014],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":90,\"y\":280,\"wires\":[[\"f093c63e17a620ba\"]]},{\"id\":\"f093c63e17a620ba\",\"type\":\"random\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"low\":\"0\",\"high\":\"100\",\"inte\":\"true\",\"property\":\"payload\",\"x\":220,\"y\":260,\"wires\":[[\"38996d8eb9f6535d\",\"936438ebf9eef986\"]]},{\"id\":\"e284b90164e648e6\",\"type\":\"ui_gauge\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"1e4a72d62ed7564c\",\"order\":2,\"width\":0,\"height\":0,\"gtype\":\"gage\",\"title\":\"gauge\",\"label\":\"units\",\"format\":\"",[2526,29017],{"value":10563},[1107,29019,28991],{},",\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":380,\"wires\":",[1107,29022],{},"},{\"id\":\"cabe7a8150dbfaf6\",\"type\":\"ui_button\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"1e4a72d62ed7564c\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":false,\"label\":\"Update\",\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":80,\"y\":380,\"wires\":[[\"7b9862186954c4b3\"]]},{\"id\":\"ac116116c56e6c3c\",\"type\":\"ui_chart\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"1e4a72d62ed7564c\",\"order\":3,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"60\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29025,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":420,\"wires\":[[]]},{\"id\":\"bb27ef41380ec1d2\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":",[1107,29028],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":90,\"y\":420,\"wires\":[[\"7b9862186954c4b3\"]]},{\"id\":\"7b9862186954c4b3\",\"type\":\"random\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"low\":\"0\",\"high\":\"100\",\"inte\":\"true\",\"property\":\"payload\",\"x\":220,\"y\":400,\"wires\":[[\"e284b90164e648e6\",\"ac116116c56e6c3c\"]]},{\"id\":\"e891f555bad51f01\",\"type\":\"ui_gauge\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"fcc4481a9e329266\",\"order\":2,\"width\":0,\"height\":0,\"gtype\":\"gage\",\"title\":\"gauge\",\"label\":\"units\",\"format\":\"",[2526,29031],{"value":10563},[1107,29033,28991],{},",\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":500,\"wires\":",[1107,29036],{},"},{\"id\":\"fa090cd1a0e97885\",\"type\":\"ui_button\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"fcc4481a9e329266\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":false,\"label\":\"Update\",\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":80,\"y\":500,\"wires\":[[\"bb4e945a2a8e681c\"]]},{\"id\":\"1c3a8aa84dfc85fe\",\"type\":\"ui_chart\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"fcc4481a9e329266\",\"order\":3,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"60\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29039,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":540,\"wires\":[[]]},{\"id\":\"796f79538c15dd66\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":",[1107,29042],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":90,\"y\":540,\"wires\":[[\"bb4e945a2a8e681c\"]]},{\"id\":\"bb4e945a2a8e681c\",\"type\":\"random\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"low\":\"0\",\"high\":\"100\",\"inte\":\"true\",\"property\":\"payload\",\"x\":220,\"y\":520,\"wires\":[[\"e891f555bad51f01\",\"1c3a8aa84dfc85fe\"]]},{\"id\":\"ba1ff527abfa5261\",\"type\":\"ui_group\",\"name\":\"Machine 1\",\"tab\":\"39383a7a648193dd\",\"order\":1,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"f6052a3dccc77ea3\",\"type\":\"ui_group\",\"name\":\"Machine 2\",\"tab\":\"39383a7a648193dd\",\"order\":2,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"1e4a72d62ed7564c\",\"type\":\"ui_group\",\"name\":\"Machine 3\",\"tab\":\"39383a7a648193dd\",\"order\":3,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"fcc4481a9e329266\",\"type\":\"ui_group\",\"name\":\"Machine 4\",\"tab\":\"39383a7a648193dd\",\"order\":4,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"39383a7a648193dd\",\"type\":\"ui_tab\",\"name\":\"Node-RED Tips\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}",[399,29045,29047],{"id":29046},"_2-add-more-than-one-series-of-data-to-a-line-chart","2. Add more than one series of data to a line chart",[15,29049,29050],{},"Being able to add more than one series of data to a single chart can make the data far more useful. One great way to use this is to compare the same data from different sensors. In this example I'm going to show the external and internal temperature at a location on the same chart.",[15,29052,29053],{},[165,29054],{"alt":29055,"src":29056,"title":29055},"Graphing two series on the same line chart","\u002Fblog\u002F2023\u002F06\u002Fimages\u002Ftemp-graph.png",[15,29058,29059],{},"To do this you need to give a different msg.topic to each series, you can add that using a change node before passing the data to the chart.",[15,29061,29062],{},"If you'd like to view this chart on your own Node-RED, you can import the flow below.",[15,29064,4917,29065,1150],{},[1107,29066,29067,29068,29070,29071,14493,29073,29085],{},"{\"id\":\"3eb08d4843164efc\",\"type\":\"ui_chart\",\"z\":\"58569b35dacd54f3\",\"name\":\"\",\"group\":\"41e847ff22249c0e\",\"order\":3,\"width\":\"12\",\"height\":\"5\",\"label\":\"Celsius\",\"chartType\":\"line\",\"legend\":\"true\",\"xformat\":\"dd HH:mm\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"604800\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29069,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":280,\"y\":180,\"wires\":[[]]},{\"id\":\"c4a13220356f76d3\",\"type\":\"inject\",\"z\":\"58569b35dacd54f3\",\"name\":\"\",\"props\":",[1107,29072,5884],{},[1107,29074,26813,29075,29078,29079,29082,29083,4957],{},[1107,29076,29077],{},"\"inside\",\"outside\"",",\"data\":[[{\"x\":1685015544647,\"y\":20.8},{\"x\":1685015844687,\"y\":20.8},{\"x\":1685016144791,\"y\":20.8},{\"x\":1685016444933,\"y\":20.8},{\"x\":1685016745032,\"y\":20.8},{\"x\":1685017045123,\"y\":20.8},{\"x\":1685017345223,\"y\":20.8},{\"x\":1685017645339,\"y\":20.8},{\"x\":1685017945424,\"y\":20.8},{\"x\":1685018245500,\"y\":20.8},{\"x\":1685018545670,\"y\":20.8},{\"x\":1685018860580,\"y\":20.8},{\"x\":1685019160814,\"y\":20.8},{\"x\":1685019460832,\"y\":20.8},{\"x\":1685019760943,\"y\":20.8},{\"x\":1685020061037,\"y\":20.8},{\"x\":1685020361249,\"y\":20.8},{\"x\":1685020661264,\"y\":20.8},{\"x\":1685020961381,\"y\":20.8},{\"x\":1685021261473,\"y\":20.8},{\"x\":1685021561721,\"y\":20.8},{\"x\":1685021876580,\"y\":20.6},{\"x\":1685022176696,\"y\":20.7},{\"x\":1685022476852,\"y\":20.8},{\"x\":1685022776957,\"y\":20.8},{\"x\":1685023077032,\"y\":20.8},{\"x\":1685023377120,\"y\":20.8},{\"x\":1685023677234,\"y\":20.8},{\"x\":1685023977339,\"y\":20.8},{\"x\":1685024277455,\"y\":20.8},{\"x\":1685024577576,\"y\":20.8},{\"x\":1685024892578,\"y\":20.8},{\"x\":1685025192616,\"y\":20.8},{\"x\":1685025492757,\"y\":20.8},{\"x\":1685025792847,\"y\":20.7},{\"x\":1685026092949,\"y\":20.8},{\"x\":1685026393052,\"y\":20.8},{\"x\":1685026693174,\"y\":20.8},{\"x\":1685026993234,\"y\":20.8},{\"x\":1685027293387,\"y\":20.8},{\"x\":1685027593514,\"y\":20.8},{\"x\":1685027908550,\"y\":20.8},{\"x\":1685028208624,\"y\":20.8},{\"x\":1685028508708,\"y\":20.8},{\"x\":1685028808828,\"y\":20.8},{\"x\":1685029108909,\"y\":20.8},{\"x\":1685029409026,\"y\":20.8},{\"x\":1685029709096,\"y\":20.8},{\"x\":1685030009262,\"y\":20.8},{\"x\":1685030309361,\"y\":20.8},{\"x\":1685030609541,\"y\":20.8},{\"x\":1685030924565,\"y\":20.8},{\"x\":1685031239588,\"y\":20.8},{\"x\":1685031539702,\"y\":20.8},{\"x\":1685031839784,\"y\":20.8},{\"x\":1685032139922,\"y\":20.8},{\"x\":1685032440027,\"y\":20.8},{\"x\":1685032740134,\"y\":20.6},{\"x\":1685033040252,\"y\":20.7},{\"x\":1685033340317,\"y\":20.5},{\"x\":1685033640450,\"y\":20.5},{\"x\":1685033940507,\"y\":20.6},{\"x\":1685034240592,\"y\":20.5},{\"x\":1685034540633,\"y\":20.4},{\"x\":1685034840784,\"y\":20.4},{\"x\":1685035140888,\"y\":20.5},{\"x\":1685035441026,\"y\":20.4},{\"x\":1685035741107,\"y\":20.5},{\"x\":1685036041205,\"y\":20.5},{\"x\":1685036341328,\"y\":20.5},{\"x\":1685036641457,\"y\":20.5},{\"x\":1685036941583,\"y\":20.4},{\"x\":1685037256578,\"y\":20.5},{\"x\":1685037556715,\"y\":20.5},{\"x\":1685037856773,\"y\":20.5},{\"x\":1685038156902,\"y\":20.5},{\"x\":1685038457023,\"y\":20.5},{\"x\":1685038757090,\"y\":20.5},{\"x\":1685039057219,\"y\":20.5},{\"x\":1685039357278,\"y\":20.5},{\"x\":1685039657449,\"y\":20.5},{\"x\":1685039957553,\"y\":20.5},{\"x\":1685040257570,\"y\":20.5},{\"x\":1685040557615,\"y\":20.5},{\"x\":1685040857735,\"y\":20.4},{\"x\":1685041157843,\"y\":20.4},{\"x\":1685041457971,\"y\":20.5},{\"x\":1685041758072,\"y\":20.5},{\"x\":1685042058154,\"y\":20.5},{\"x\":1685042358273,\"y\":20.5},{\"x\":1685042658392,\"y\":20.5},{\"x\":1685042958486,\"y\":20.6},{\"x\":1685043258518,\"y\":20.6},{\"x\":1685043558568,\"y\":20.6},{\"x\":1685043858688,\"y\":20.5},{\"x\":1685044158769,\"y\":20.5},{\"x\":1685044458908,\"y\":20.5},{\"x\":1685044758984,\"y\":20.5},{\"x\":1685045059095,\"y\":20.5},{\"x\":1685045359169,\"y\":20.5},{\"x\":1685045659320,\"y\":20.5},{\"x\":1685045959367,\"y\":20.5},{\"x\":1685046259469,\"y\":20.7},{\"x\":1685046559497,\"y\":20.6},{\"x\":1685046859589,\"y\":20.5},{\"x\":1685047159647,\"y\":20.6},{\"x\":1685047459714,\"y\":20.6},{\"x\":1685047759793,\"y\":20.7},{\"x\":1685048059921,\"y\":20.7},{\"x\":1685048359994,\"y\":20.6},{\"x\":1685048659997,\"y\":20.7},{\"x\":1685048960056,\"y\":20.7},{\"x\":1685049260070,\"y\":20.8},{\"x\":1685049560116,\"y\":20.8},{\"x\":1685049860167,\"y\":20.8},{\"x\":1685050160212,\"y\":20.8},{\"x\":1685050460281,\"y\":20.8},{\"x\":1685050760368,\"y\":20.8},{\"x\":1685051060400,\"y\":20.7},{\"x\":1685051360435,\"y\":20.8},{\"x\":1685051660553,\"y\":20.8},{\"x\":1685051960585,\"y\":20.8},{\"x\":1685052260665,\"y\":20.8},{\"x\":1685052560675,\"y\":20.8},{\"x\":1685052860700,\"y\":20.7},{\"x\":1685053160749,\"y\":20.8},{\"x\":1685053460790,\"y\":20.8},{\"x\":1685053760855,\"y\":20.7},{\"x\":1685054060931,\"y\":20.7},{\"x\":1685054361019,\"y\":20.7},{\"x\":1685054661030,\"y\":20.7},{\"x\":1685054961121,\"y\":20.7},{\"x\":1685055261227,\"y\":20.7},{\"x\":1685055576250,\"y\":20.7},{\"x\":1685055891289,\"y\":20.5},{\"x\":1685056191290,\"y\":20.6},{\"x\":1685056491397,\"y\":20.7},{\"x\":1685056791444,\"y\":20.7},{\"x\":1685057091489,\"y\":20.7},{\"x\":1685057391516,\"y\":20.7},{\"x\":1685057691607,\"y\":20.7},{\"x\":1685057991660,\"y\":20.7},{\"x\":1685058291741,\"y\":20.6},{\"x\":1685058606760,\"y\":20.5},{\"x\":1685058906775,\"y\":20.5},{\"x\":1685059206866,\"y\":20.5},{\"x\":1685059506900,\"y\":20.5},{\"x\":1685059806979,\"y\":20.4},{\"x\":1685060107035,\"y\":20.5},{\"x\":1685060407099,\"y\":20.5},{\"x\":1685060707132,\"y\":20.4},{\"x\":1685061007199,\"y\":20.5},{\"x\":1685061307220,\"y\":20.4},{\"x\":1685061607228,\"y\":20.5},{\"x\":1685061907286,\"y\":20.6},{\"x\":1685062207289,\"y\":20.7},{\"x\":1685062507315,\"y\":20.6},{\"x\":1685062807355,\"y\":20.7},{\"x\":1685063107395,\"y\":20.7},{\"x\":1685063407413,\"y\":20.7},{\"x\":1685063707465,\"y\":20.7},{\"x\":1685064007492,\"y\":20.7},{\"x\":1685064307565,\"y\":20.7},{\"x\":1685064607587,\"y\":20.7},{\"x\":1685064907609,\"y\":20.7},{\"x\":1685065207618,\"y\":20.5},{\"x\":1685065507642,\"y\":20.4},{\"x\":1685065807670,\"y\":20.4},{\"x\":1685066107679,\"y\":20.4},{\"x\":1685066407758,\"y\":20.5},{\"x\":1685066707760,\"y\":20.3},{\"x\":1685067007798,\"y\":20.4},{\"x\":1685067307865,\"y\":20.4},{\"x\":1685067622903,\"y\":20.4},{\"x\":1685067937922,\"y\":20.4},{\"x\":1685068237945,\"y\":20.4},{\"x\":1685068537971,\"y\":20.3},{\"x\":1685068838009,\"y\":20.3},{\"x\":1685069138060,\"y\":20.2},{\"x\":1685069453065,\"y\":20.3},{\"x\":1685069753104,\"y\":20.3},{\"x\":1685070053108,\"y\":20.2},{\"x\":1685070353140,\"y\":20.3},{\"x\":1685070653170,\"y\":20.2},{\"x\":1685070953219,\"y\":20.2},{\"x\":1685071253232,\"y\":20.2},{\"x\":1685071553258,\"y\":20},{\"x\":1685071853260,\"y\":20},{\"x\":1685072153324,\"y\":20.2},{\"x\":1685072453329,\"y\":20.2},{\"x\":1685072753343,\"y\":19.9},{\"x\":1685073053359,\"y\":20},{\"x\":1685073353402,\"y\":19.9},{\"x\":1685073653411,\"y\":19.9},{\"x\":1685073953453,\"y\":20},{\"x\":1685074253462,\"y\":20},{\"x\":1685074553509,\"y\":20},{\"x\":1685074853529,\"y\":20},{\"x\":1685075153554,\"y\":20},{\"x\":1685075453556,\"y\":20},{\"x\":1685075753590,\"y\":20},{\"x\":1685076053642,\"y\":20.2},{\"x\":1685076368644,\"y\":20},{\"x\":1685076668676,\"y\":20.2},{\"x\":1685076968685,\"y\":20.3},{\"x\":1685077268752,\"y\":20.3},{\"x\":1685077583729,\"y\":20.3},{\"x\":1685077883800,\"y\":20.2},{\"x\":1685078198760,\"y\":20.2},{\"x\":1685078498762,\"y\":20.4},{\"x\":1685078798782,\"y\":20.4},{\"x\":1685079098837,\"y\":20.5},{\"x\":1685079398903,\"y\":20.5},{\"x\":1685079698965,\"y\":20.6},{\"x\":1685079999013,\"y\":20.7},{\"x\":1685080299091,\"y\":20.8},{\"x\":1685080599234,\"y\":20.8},{\"x\":1685080899276,\"y\":20.8},{\"x\":1685081199340,\"y\":20.8},{\"x\":1685081499403,\"y\":20.8},{\"x\":1685081799441,\"y\":20.8},{\"x\":1685082099467,\"y\":20.9},{\"x\":1685082399584,\"y\":20.8},{\"x\":1685082699624,\"y\":21},{\"x\":1685082999670,\"y\":21},{\"x\":1685083299724,\"y\":21.2},{\"x\":1685083599810,\"y\":21.2},{\"x\":1685083899884,\"y\":21.2},{\"x\":1685084199935,\"y\":21.2},{\"x\":1685084500001,\"y\":21.3},{\"x\":1685084800063,\"y\":21.4},{\"x\":1685085115072,\"y\":21.5},{\"x\":1685085415170,\"y\":21.5},{\"x\":1685085715226,\"y\":21.5},{\"x\":1685086015289,\"y\":21.5},{\"x\":1685086315365,\"y\":21.5},{\"x\":1685086615447,\"y\":21.5},{\"x\":1685086915501,\"y\":21.7},{\"x\":1685087215603,\"y\":21.7},{\"x\":1685087515642,\"y\":21.6},{\"x\":1685087815732,\"y\":21.6},{\"x\":1685088115759,\"y\":21.7},{\"x\":1685088415793,\"y\":21.9},{\"x\":1685088715840,\"y\":21.9},{\"x\":1685089015891,\"y\":21.9},{\"x\":1685089315950,\"y\":21.9},{\"x\":1685089616045,\"y\":21.9},{\"x\":1685089916094,\"y\":21.9},{\"x\":1685090216153,\"y\":21.9},{\"x\":1685090516202,\"y\":21.9},{\"x\":1685090816299,\"y\":21.9},{\"x\":1685091116304,\"y\":22},{\"x\":1685091416326,\"y\":22},{\"x\":1685091716409,\"y\":22},{\"x\":1685092016500,\"y\":22},{\"x\":1685092316555,\"y\":21.9},{\"x\":1685092616597,\"y\":21.9},{\"x\":1685092916687,\"y\":21.9},{\"x\":1685093216749,\"y\":21.9},{\"x\":1685093516770,\"y\":21.9},{\"x\":1685093816833,\"y\":21.8},{\"x\":1685094116862,\"y\":21.8},{\"x\":1685094416941,\"y\":21.9},{\"x\":1685094717014,\"y\":21.8},{\"x\":1685095017079,\"y\":21.8},{\"x\":1685095317146,\"y\":21.9},{\"x\":1685095617198,\"y\":21.9},{\"x\":1685095917267,\"y\":22},{\"x\":1685096217380,\"y\":22},{\"x\":1685096517436,\"y\":22.2},{\"x\":1685096817481,\"y\":22.1},{\"x\":1685097117519,\"y\":21.9},{\"x\":1685097417582,\"y\":21.9},{\"x\":1685097717607,\"y\":21.9},{\"x\":1685098017734,\"y\":21.9},{\"x\":1685098317791,\"y\":21.9},{\"x\":1685098617850,\"y\":21.9},{\"x\":1685098917939,\"y\":21.9},{\"x\":1685099218020,\"y\":21.9},{\"x\":1685099518088,\"y\":21.9},{\"x\":1685099818190,\"y\":21.9},{\"x\":1685100118269,\"y\":21.9},{\"x\":1685100418304,\"y\":21.9},{\"x\":1685100718355,\"y\":21.9},{\"x\":1685101018437,\"y\":22},{\"x\":1685101318544,\"y\":22.1},{\"x\":1685101618646,\"y\":22},{\"x\":1685101918720,\"y\":22},{\"x\":1685102218803,\"y\":22},{\"x\":1685102518914,\"y\":21.9},{\"x\":1685102818997,\"y\":21.9},{\"x\":1685103119093,\"y\":21.9},{\"x\":1685103419119,\"y\":21.9},{\"x\":1685103719183,\"y\":21.9},{\"x\":1685104019263,\"y\":21.9},{\"x\":1685104319361,\"y\":21.9},{\"x\":1685104619467,\"y\":21.9},{\"x\":1685104919565,\"y\":21.9},{\"x\":1685105219692,\"y\":21.9},{\"x\":1685105261657,\"y\":21.9},{\"x\":1685105275726,\"y\":21.9},{\"x\":1685105316848,\"y\":21.9},{\"x\":1685105388676,\"y\":21.9},{\"x\":1685105475001,\"y\":21.9},{\"x\":1685105501466,\"y\":21.9},{\"x\":1685105576628,\"y\":21.9},{\"x\":1685105627162,\"y\":21.9},{\"x\":1685105653330,\"y\":21.9},{\"x\":1685105684112,\"y\":21.9},{\"x\":1685105722222,\"y\":21.9},{\"x\":1685105769629,\"y\":21.9},{\"x\":1685105825666,\"y\":21.9},{\"x\":1685105889137,\"y\":21.9},{\"x\":1685105910507,\"y\":21.9},{\"x\":1685105970749,\"y\":21.9},{\"x\":1685106270828,\"y\":21.9},{\"x\":1685106570844,\"y\":21.9},{\"x\":1685106870845,\"y\":21.9},{\"x\":1685107170872,\"y\":21.9},{\"x\":1685107485846,\"y\":21.9},{\"x\":1685107785884,\"y\":21.9},{\"x\":1685108100859,\"y\":21.9},{\"x\":1685108400859,\"y\":21.9},{\"x\":1685108700881,\"y\":21.9},{\"x\":1685109000885,\"y\":21.9},{\"x\":1685109300898,\"y\":21.9},{\"x\":1685109615915,\"y\":21.8},{\"x\":1685109930873,\"y\":21.9},{\"x\":1685110230928,\"y\":21.9},{\"x\":1685110545931,\"y\":21.7},{\"x\":1685110845948,\"y\":21.7},{\"x\":1685111145989,\"y\":21.7},{\"x\":1685111460988,\"y\":21.7},{\"x\":1685111761062,\"y\":21.8},{\"x\":1685112061067,\"y\":21.6},{\"x\":1685112361107,\"y\":21.7},{\"x\":1685112661141,\"y\":21.7},{\"x\":1685112961145,\"y\":21.8},{\"x\":1685113261191,\"y\":21.8},{\"x\":1685113561195,\"y\":21.8},{\"x\":1685113861234,\"y\":21.8},{\"x\":1685114161272,\"y\":21.8},{\"x\":1685114461288,\"y\":21.7},{\"x\":1685114761326,\"y\":21.7},{\"x\":1685115061359,\"y\":21.7},{\"x\":1685115361389,\"y\":21.7},{\"x\":1685115661413,\"y\":21.6},{\"x\":1685115961522,\"y\":21.7},{\"x\":1685116276440,\"y\":21.7},{\"x\":1685116576471,\"y\":21.5},{\"x\":1685116876500,\"y\":21.5},{\"x\":1685117176511,\"y\":21.5},{\"x\":1685117476538,\"y\":21.6},{\"x\":1685117776599,\"y\":21.6},{\"x\":1685118091580,\"y\":21.8},{\"x\":1685118391620,\"y\":21.6},{\"x\":1685118691646,\"y\":21.7},{\"x\":1685118991656,\"y\":21.7},{\"x\":1685119291661,\"y\":21.5},{\"x\":1685119591684,\"y\":21.5},{\"x\":1685119891711,\"y\":21.5},{\"x\":1685120191732,\"y\":21.5},{\"x\":1685120491763,\"y\":21.5},{\"x\":1685120791797,\"y\":21.4},{\"x\":1685121091832,\"y\":21.2},{\"x\":1685121406802,\"y\":21.2},{\"x\":1685121706853,\"y\":21.1},{\"x\":1685122021885,\"y\":21.1},{\"x\":1685122336872,\"y\":21.2},{\"x\":1685122636878,\"y\":21.2},{\"x\":1685122936903,\"y\":21.1},{\"x\":1685123236933,\"y\":21},{\"x\":1685123536988,\"y\":21},{\"x\":1685123837007,\"y\":21.1},{\"x\":1685124137007,\"y\":21},{\"x\":1685124437098,\"y\":21},{\"x\":1685124737193,\"y\":21},{\"x\":1685125037270,\"y\":21},{\"x\":1685125337370,\"y\":20.9},{\"x\":1685125637493,\"y\":20.9},{\"x\":1685125937530,\"y\":21},{\"x\":1685126237627,\"y\":20.9},{\"x\":1685126537712,\"y\":20.8},{\"x\":1685126837733,\"y\":20.8},{\"x\":1685127137746,\"y\":20.8},{\"x\":1685127437870,\"y\":20.8},{\"x\":1685127737940,\"y\":20.8},{\"x\":1685128038026,\"y\":20.8},{\"x\":1685128338109,\"y\":20.8},{\"x\":1685128638215,\"y\":20.8},{\"x\":1685128938321,\"y\":20.8},{\"x\":1685129238409,\"y\":20.8},{\"x\":1685129538484,\"y\":20.8},{\"x\":1685129838514,\"y\":20.8},{\"x\":1685130138614,\"y\":20.8},{\"x\":1685130438644,\"y\":20.8},{\"x\":1685130738716,\"y\":20.8},{\"x\":1685131038856,\"y\":20.8},{\"x\":1685131338912,\"y\":20.8},{\"x\":1685131639028,\"y\":20.8},{\"x\":1685131939140,\"y\":20.8},{\"x\":1685132239222,\"y\":20.8},{\"x\":1685132539344,\"y\":20.8},{\"x\":1685132839390,\"y\":20.8},{\"x\":1685133154381,\"y\":20.8},{\"x\":1685133454402,\"y\":20.8},{\"x\":1685133754494,\"y\":20.8},{\"x\":1685134054557,\"y\":20.8},{\"x\":1685134354644,\"y\":20.8},{\"x\":1685134654753,\"y\":20.8},{\"x\":1685134954810,\"y\":20.8},{\"x\":1685135254894,\"y\":20.7},{\"x\":1685135554961,\"y\":20.8},{\"x\":1685135854990,\"y\":20.8},{\"x\":1685136155013,\"y\":20.8},{\"x\":1685136455070,\"y\":20.8},{\"x\":1685136755172,\"y\":20.8},{\"x\":1685137055242,\"y\":20.8},{\"x\":1685137355339,\"y\":20.8},{\"x\":1685137655440,\"y\":20.7},{\"x\":1685137955504,\"y\":20.7},{\"x\":1685138255592,\"y\":20.6},{\"x\":1685138555675,\"y\":20.6},{\"x\":1685138855742,\"y\":20.6},{\"x\":1685139155750,\"y\":20.6},{\"x\":1685139455841,\"y\":20.5},{\"x\":1685139755904,\"y\":20.6},{\"x\":1685140055961,\"y\":20.5},{\"x\":1685140356100,\"y\":20.6},{\"x\":1685140656187,\"y\":20.5},{\"x\":1685140956268,\"y\":20.4},{\"x\":1685141256384,\"y\":20.4},{\"x\":1685141556467,\"y\":20.4},{\"x\":1685141856547,\"y\":20.4},{\"x\":1685142156630,\"y\":20.3},{\"x\":1685142456678,\"y\":20.2},{\"x\":1685142756713,\"y\":20.3},{\"x\":1685143056833,\"y\":20.3},{\"x\":1685143356922,\"y\":20.3},{\"x\":1685143657019,\"y\":20.3},{\"x\":1685143957116,\"y\":20.2},{\"x\":1685144257203,\"y\":20.2},{\"x\":1685144557262,\"y\":20.1},{\"x\":1685144857393,\"y\":20.2},{\"x\":1685145157511,\"y\":20.2},{\"x\":1685145457515,\"y\":20.2},{\"x\":1685145757583,\"y\":20},{\"x\":1685146057687,\"y\":20},{\"x\":1685146357780,\"y\":20.1},{\"x\":1685146657878,\"y\":19.9},{\"x\":1685146957969,\"y\":19.9},{\"x\":1685147258066,\"y\":20},{\"x\":1685147558163,\"y\":20},{\"x\":1685147858255,\"y\":19.9},{\"x\":1685148158303,\"y\":19.9},{\"x\":1685148458377,\"y\":19.9},{\"x\":1685148758392,\"y\":19.9},{\"x\":1685149058441,\"y\":19.9},{\"x\":1685149358550,\"y\":19.9},{\"x\":1685149658652,\"y\":19.5},{\"x\":1685149958735,\"y\":19.5},{\"x\":1685150258816,\"y\":19.5},{\"x\":1685150558892,\"y\":19.5},{\"x\":1685150858996,\"y\":19.5},{\"x\":1685151159109,\"y\":19.5},{\"x\":1685151459166,\"y\":19.5},{\"x\":1685151759230,\"y\":19.5},{\"x\":1685152059291,\"y\":19.5},{\"x\":1685152359350,\"y\":19.5},{\"x\":1685152659430,\"y\":19.5},{\"x\":1685152959526,\"y\":19.5},{\"x\":1685153259622,\"y\":19.5},{\"x\":1685153559728,\"y\":19.5},{\"x\":1685153859883,\"y\":19.5},{\"x\":1685154159899,\"y\":19.5},{\"x\":1685154459974,\"y\":19.5},{\"x\":1685154760016,\"y\":19.5},{\"x\":1685155060096,\"y\":19.5},{\"x\":1685155360142,\"y\":19.5},{\"x\":1685155660284,\"y\":19.5},{\"x\":1685155960372,\"y\":19.5},{\"x\":1685156260469,\"y\":19.5},{\"x\":1685156560559,\"y\":19.5},{\"x\":1685156860664,\"y\":19.5},{\"x\":1685157160732,\"y\":19.5},{\"x\":1685157460827,\"y\":19.5},{\"x\":1685157760925,\"y\":19.5},{\"x\":1685158061041,\"y\":19.4},{\"x\":1685158361093,\"y\":19.4},{\"x\":1685158661174,\"y\":19.4},{\"x\":1685158961238,\"y\":19.4},{\"x\":1685159261356,\"y\":19.4},{\"x\":1685159561460,\"y\":19.4},{\"x\":1685159861591,\"y\":19.4},{\"x\":1685160161622,\"y\":19.5},{\"x\":1685160461727,\"y\":19.4},{\"x\":1685160761798,\"y\":19.3},{\"x\":1685161061899,\"y\":19.3},{\"x\":1685161361960,\"y\":19.3},{\"x\":1685161662030,\"y\":19.3},{\"x\":1685161962083,\"y\":19.2},{\"x\":1685162262205,\"y\":19.2},{\"x\":1685162562329,\"y\":19.2},{\"x\":1685162862420,\"y\":19.2},{\"x\":1685163162514,\"y\":19.3},{\"x\":1685163462630,\"y\":19.4},{\"x\":1685163762689,\"y\":19.5},{\"x\":1685164062785,\"y\":19.5},{\"x\":1685164362862,\"y\":19.5},{\"x\":1685164662929,\"y\":19.5},{\"x\":1685164962989,\"y\":19.5},{\"x\":1685165263080,\"y\":19.5},{\"x\":1685165563213,\"y\":19.5},{\"x\":1685165863284,\"y\":19.5},{\"x\":1685166163398,\"y\":19.7},{\"x\":1685166463432,\"y\":20},{\"x\":1685166763562,\"y\":20},{\"x\":1685167063657,\"y\":20.3},{\"x\":1685167363752,\"y\":20.3},{\"x\":1685167663818,\"y\":20.2},{\"x\":1685167963868,\"y\":20.4},{\"x\":1685168263959,\"y\":20.5},{\"x\":1685168564052,\"y\":20.7},{\"x\":1685168864152,\"y\":20.8},{\"x\":1685169164297,\"y\":20.8},{\"x\":1685169464371,\"y\":20.8},{\"x\":1685169764428,\"y\":20.9},{\"x\":1685170064553,\"y\":21},{\"x\":1685170364630,\"y\":20.8},{\"x\":1685170664689,\"y\":20.8},{\"x\":1685170964744,\"y\":20.9},{\"x\":1685171264839,\"y\":21},{\"x\":1685171564944,\"y\":21},{\"x\":1685171865046,\"y\":21.1},{\"x\":1685172165136,\"y\":21.1},{\"x\":1685172465216,\"y\":21.2},{\"x\":1685172765270,\"y\":21.5},{\"x\":1685173065378,\"y\":21.5},{\"x\":1685173365491,\"y\":21.5},{\"x\":1685173665574,\"y\":21.5},{\"x\":1685173965634,\"y\":21.5},{\"x\":1685174265651,\"y\":21.5},{\"x\":1685174565770,\"y\":21.5},{\"x\":1685174865842,\"y\":21.5},{\"x\":1685175165962,\"y\":21.5},{\"x\":1685175466056,\"y\":21.5},{\"x\":1685175766151,\"y\":21.6},{\"x\":1685176066247,\"y\":21.7},{\"x\":1685176366347,\"y\":21.6},{\"x\":1685176666421,\"y\":21.6},{\"x\":1685176966481,\"y\":21.7},{\"x\":1685177266535,\"y\":21.7},{\"x\":1685177566618,\"y\":21.7},{\"x\":1685177866715,\"y\":21.8},{\"x\":1685178166786,\"y\":21.7},{\"x\":1685178466868,\"y\":21.7},{\"x\":1685178766964,\"y\":21.8},{\"x\":1685179067013,\"y\":21.9},{\"x\":1685179367161,\"y\":21.9},{\"x\":1685179667226,\"y\":21.9},{\"x\":1685179967298,\"y\":21.9},{\"x\":1685180267304,\"y\":21.9},{\"x\":1685180567400,\"y\":21.9},{\"x\":1685180867474,\"y\":21.9},{\"x\":1685181167605,\"y\":21.9},{\"x\":1685181467661,\"y\":21.9},{\"x\":1685181767781,\"y\":21.9},{\"x\":1685182067847,\"y\":21.9},{\"x\":1685182367916,\"y\":21.9},{\"x\":1685182668010,\"y\":21.9},{\"x\":1685182968067,\"y\":21.9},{\"x\":1685183268142,\"y\":21.9},{\"x\":1685183568218,\"y\":21.9},{\"x\":1685183868264,\"y\":21.9},{\"x\":1685184168356,\"y\":22},{\"x\":1685184468449,\"y\":22},{\"x\":1685184768557,\"y\":22},{\"x\":1685185068624,\"y\":22.2},{\"x\":1685185368711,\"y\":22.1},{\"x\":1685185668784,\"y\":22.1},{\"x\":1685185968847,\"y\":22},{\"x\":1685186268953,\"y\":21.9},{\"x\":1685186568982,\"y\":21.9},{\"x\":1685186868996,\"y\":21.9},{\"x\":1685187169094,\"y\":21.9},{\"x\":1685187469146,\"y\":21.9},{\"x\":1685187769224,\"y\":21.9},{\"x\":1685188069250,\"y\":21.9},{\"x\":1685188369327,\"y\":21.9},{\"x\":1685188669411,\"y\":21.7},{\"x\":1685188969475,\"y\":21.7},{\"x\":1685189269500,\"y\":21.5},{\"x\":1685189569548,\"y\":21.5},{\"x\":1685189869583,\"y\":21.6},{\"x\":1685190169645,\"y\":21.7},{\"x\":1685190469717,\"y\":21.7},{\"x\":1685190769801,\"y\":21.7},{\"x\":1685191069848,\"y\":21.7},{\"x\":1685191369885,\"y\":21.7},{\"x\":1685191669980,\"y\":21.7},{\"x\":1685191970014,\"y\":21.7},{\"x\":1685192270055,\"y\":21.6},{\"x\":1685192570106,\"y\":21.7},{\"x\":1685192870150,\"y\":21.7},{\"x\":1685193170235,\"y\":21.6},{\"x\":1685193470249,\"y\":21.5},{\"x\":1685193770302,\"y\":21.5},{\"x\":1685194070311,\"y\":21.6},{\"x\":1685194370328,\"y\":21.7},{\"x\":1685194670393,\"y\":21.7},{\"x\":1685194985377,\"y\":21.7},{\"x\":1685195285440,\"y\":21.7},{\"x\":1685195585471,\"y\":21.7},{\"x\":1685195885473,\"y\":21.7},{\"x\":1685196185514,\"y\":21.6},{\"x\":1685196485562,\"y\":21.7},{\"x\":1685196800615,\"y\":21.7},{\"x\":1685197115572,\"y\":21.7},{\"x\":1685197415601,\"y\":21.7},{\"x\":1685197715640,\"y\":21.7},{\"x\":1685198015652,\"y\":21.7},{\"x\":1685198315706,\"y\":21.7},{\"x\":1685198615725,\"y\":21.7},{\"x\":1685198915775,\"y\":21.8},{\"x\":1685199215786,\"y\":21.8},{\"x\":1685199515831,\"y\":21.9},{\"x\":1685199815856,\"y\":21.8},{\"x\":1685200115858,\"y\":21.9},{\"x\":1685200415875,\"y\":21.8},{\"x\":1685200715913,\"y\":21.8},{\"x\":1685201015918,\"y\":21.8},{\"x\":1685201315964,\"y\":21.9},{\"x\":1685201616032,\"y\":21.9},{\"x\":1685201916044,\"y\":21.9},{\"x\":1685202231051,\"y\":21.9},{\"x\":1685202531062,\"y\":21.9},{\"x\":1685202831089,\"y\":21.8},{\"x\":1685203131095,\"y\":21.8},{\"x\":1685203431195,\"y\":21.8},{\"x\":1685203746138,\"y\":21.8},{\"x\":1685204046168,\"y\":21.8},{\"x\":1685204346182,\"y\":21.7},{\"x\":1685204646203,\"y\":21.8},{\"x\":1685204946241,\"y\":21.9},{\"x\":1685205246243,\"y\":21.9},{\"x\":1685205546262,\"y\":21.9},{\"x\":1685205846296,\"y\":21.9},{\"x\":1685206146312,\"y\":21.9},{\"x\":1685206446335,\"y\":21.9},{\"x\":1685206746375,\"y\":21.9},{\"x\":1685207046395,\"y\":21.9},{\"x\":1685207346549,\"y\":21.9},{\"x\":1685207661544,\"y\":21.9},{\"x\":1685207961647,\"y\":21.9},{\"x\":1685208261700,\"y\":21.9},{\"x\":1685208561766,\"y\":21.9},{\"x\":1685208861855,\"y\":21.9},{\"x\":1685209161943,\"y\":21.9},{\"x\":1685209462007,\"y\":21.9},{\"x\":1685209762097,\"y\":21.9},{\"x\":1685210077101,\"y\":21.9},{\"x\":1685210377152,\"y\":21.9},{\"x\":1685210677231,\"y\":21.9},{\"x\":1685210977296,\"y\":21.9},{\"x\":1685211277406,\"y\":21.9},{\"x\":1685211577537,\"y\":21.9},{\"x\":1685211877620,\"y\":21.9},{\"x\":1685212177660,\"y\":21.9},{\"x\":1685212477797,\"y\":21.9},{\"x\":1685212777837,\"y\":21.9},{\"x\":1685213077881,\"y\":21.9},{\"x\":1685213377932,\"y\":21.9},{\"x\":1685213677997,\"y\":21.9},{\"x\":1685213978205,\"y\":21.9},{\"x\":1685214293200,\"y\":21.9},{\"x\":1685214593270,\"y\":21.9},{\"x\":1685214893375,\"y\":21.9},{\"x\":1685215193482,\"y\":21.9},{\"x\":1685215493529,\"y\":21.8},{\"x\":1685215793531,\"y\":21.9},{\"x\":1685216093643,\"y\":21.9},{\"x\":1685216393738,\"y\":22},{\"x\":1685216693784,\"y\":21.9},{\"x\":1685216993880,\"y\":21.9},{\"x\":1685217293962,\"y\":21.7},{\"x\":1685217594100,\"y\":21.7},{\"x\":1685217894151,\"y\":21.7},{\"x\":1685218194256,\"y\":21.6},{\"x\":1685218494376,\"y\":21.5},{\"x\":1685218794467,\"y\":21.5},{\"x\":1685219094477,\"y\":21.5},{\"x\":1685219394675,\"y\":21.5},{\"x\":1685219709688,\"y\":21.5},{\"x\":1685220009789,\"y\":21.5},{\"x\":1685220309889,\"y\":21.5},{\"x\":1685220609986,\"y\":21.4},{\"x\":1685220910080,\"y\":21.5},{\"x\":1685221210236,\"y\":21.5},{\"x\":1685221510267,\"y\":21.5},{\"x\":1685221810331,\"y\":21.5},{\"x\":1685222110381,\"y\":21.4},{\"x\":1685222410481,\"y\":21.4},{\"x\":1685222710598,\"y\":21.3},{\"x\":1685223010661,\"y\":21.5},{\"x\":1685223310759,\"y\":21.5},{\"x\":1685223610881,\"y\":21.5},{\"x\":1685223911009,\"y\":21.5},{\"x\":1685224211106,\"y\":21.5},{\"x\":1685224511191,\"y\":21.5},{\"x\":1685224811272,\"y\":21.4},{\"x\":1685225111309,\"y\":21.4},{\"x\":1685225411398,\"y\":21.3},{\"x\":1685225711438,\"y\":21.2},{\"x\":1685226011552,\"y\":21.2},{\"x\":1685226311659,\"y\":21.2},{\"x\":1685226611773,\"y\":21.2},{\"x\":1685226911876,\"y\":21.2},{\"x\":1685227211995,\"y\":21.2},{\"x\":1685227512070,\"y\":21.2},{\"x\":1685227812175,\"y\":21.2},{\"x\":1685228112250,\"y\":21.2},{\"x\":1685228412314,\"y\":21},{\"x\":1685228712352,\"y\":21},{\"x\":1685229012470,\"y\":21},{\"x\":1685229312587,\"y\":21},{\"x\":1685229612662,\"y\":21.1},{\"x\":1685229912736,\"y\":21},{\"x\":1685230212847,\"y\":20.9},{\"x\":1685230512904,\"y\":21},{\"x\":1685230813034,\"y\":21},{\"x\":1685231113081,\"y\":21},{\"x\":1685231413122,\"y\":20.8},{\"x\":1685231713183,\"y\":20.8},{\"x\":1685232013342,\"y\":20.8},{\"x\":1685232328321,\"y\":20.8},{\"x\":1685232628419,\"y\":20.8},{\"x\":1685232928528,\"y\":20.8},{\"x\":1685233228604,\"y\":20.8},{\"x\":1685233528687,\"y\":20.8},{\"x\":1685233828743,\"y\":20.8},{\"x\":1685234128809,\"y\":20.8},{\"x\":1685234428883,\"y\":20.8},{\"x\":1685234728963,\"y\":20.8},{\"x\":1685235028977,\"y\":20.8},{\"x\":1685235329064,\"y\":20.8},{\"x\":1685235629163,\"y\":20.8},{\"x\":1685235929251,\"y\":20.8},{\"x\":1685236229341,\"y\":20.8},{\"x\":1685236529423,\"y\":20.7},{\"x\":1685236829513,\"y\":20.7},{\"x\":1685237129612,\"y\":20.8},{\"x\":1685237429697,\"y\":20.7},{\"x\":1685237729771,\"y\":20.7},{\"x\":1685238029832,\"y\":20.5},{\"x\":1685238329902,\"y\":20.5},{\"x\":1685238629974,\"y\":20.5},{\"x\":1685238930096,\"y\":20.5},{\"x\":1685239230144,\"y\":20.6},{\"x\":1685239530225,\"y\":20.6},{\"x\":1685239830331,\"y\":20.6},{\"x\":1685240130366,\"y\":20.6},{\"x\":1685240430509,\"y\":20.5},{\"x\":1685240730597,\"y\":20.5},{\"x\":1685241030697,\"y\":20.4},{\"x\":1685241330716,\"y\":20.4},{\"x\":1685241630792,\"y\":20.4},{\"x\":1685241930839,\"y\":20.4},{\"x\":1685242230957,\"y\":20.4},{\"x\":1685242531061,\"y\":20.4},{\"x\":1685242831116,\"y\":20.4},{\"x\":1685243131245,\"y\":20.4},{\"x\":1685243431358,\"y\":20.4},{\"x\":1685243731422,\"y\":20.3},{\"x\":1685244031484,\"y\":20.3},{\"x\":1685244331554,\"y\":20.2},{\"x\":1685244631614,\"y\":20.3},{\"x\":1685244931700,\"y\":20.3},{\"x\":1685245231790,\"y\":20.3},{\"x\":1685245531908,\"y\":20.3},{\"x\":1685245832059,\"y\":20.3},{\"x\":1685246132107,\"y\":20.3},{\"x\":1685246432206,\"y\":20.3},{\"x\":1685246732334,\"y\":20.3},{\"x\":1685247032445,\"y\":20.3},{\"x\":1685247332472,\"y\":20.2},{\"x\":1685247632538,\"y\":20.2},{\"x\":1685247932668,\"y\":20.2},{\"x\":1685248232731,\"y\":20.2},{\"x\":1685248532806,\"y\":20.2},{\"x\":1685248832930,\"y\":20.2},{\"x\":1685249133015,\"y\":20.2},{\"x\":1685249433093,\"y\":20.2},{\"x\":1685249733189,\"y\":20.2},{\"x\":1685250033291,\"y\":20.1},{\"x\":1685250333378,\"y\":20},{\"x\":1685250633446,\"y\":20},{\"x\":1685250933493,\"y\":20},{\"x\":1685251233569,\"y\":20},{\"x\":1685251533627,\"y\":20},{\"x\":1685251833774,\"y\":20.1},{\"x\":1685252133852,\"y\":20.2},{\"x\":1685252433955,\"y\":20.2},{\"x\":1685252734091,\"y\":20.2},{\"x\":1685253034179,\"y\":20},{\"x\":1685253334257,\"y\":19.9},{\"x\":1685253634325,\"y\":20},{\"x\":1685253934367,\"y\":20.2},{\"x\":1685254234489,\"y\":20.2},{\"x\":1685254534587,\"y\":20.2},{\"x\":1685254834680,\"y\":20.2},{\"x\":1685255134805,\"y\":20.3},{\"x\":1685255434885,\"y\":20.3},{\"x\":1685255735002,\"y\":20.3},{\"x\":1685256035085,\"y\":20.5},{\"x\":1685256335179,\"y\":20.5},{\"x\":1685256635254,\"y\":20.3},{\"x\":1685256935309,\"y\":20.3},{\"x\":1685257235338,\"y\":20.3},{\"x\":1685257535440,\"y\":20.3},{\"x\":1685257835520,\"y\":20.4},{\"x\":1685258135635,\"y\":20.5},{\"x\":1685258435709,\"y\":20.5},{\"x\":1685258735790,\"y\":20.5},{\"x\":1685259035904,\"y\":20.5},{\"x\":1685259335965,\"y\":20.4},{\"x\":1685259636005,\"y\":20.4},{\"x\":1685259936052,\"y\":20.4},{\"x\":1685260236148,\"y\":20.5},{\"x\":1685260536249,\"y\":20.4},{\"x\":1685260836320,\"y\":20.5},{\"x\":1685261136489,\"y\":20.5},{\"x\":1685261436489,\"y\":20.6},{\"x\":1685261736607,\"y\":20.5},{\"x\":1685262036710,\"y\":20.5},{\"x\":1685262336806,\"y\":20.4},{\"x\":1685262636943,\"y\":20.4},{\"x\":1685262936991,\"y\":20.3},{\"x\":1685263237019,\"y\":20.3},{\"x\":1685263537113,\"y\":20.3},{\"x\":1685263837223,\"y\":20.3},{\"x\":1685264137303,\"y\":20.3},{\"x\":1685264437449,\"y\":20.4},{\"x\":1685264737520,\"y\":20.3},{\"x\":1685265037596,\"y\":20.4},{\"x\":1685265337653,\"y\":20.3},{\"x\":1685265637725,\"y\":20.3},{\"x\":1685265937773,\"y\":20.3},{\"x\":1685266237884,\"y\":20.3},{\"x\":1685266537917,\"y\":20.3},{\"x\":1685266837940,\"y\":20.3},{\"x\":1685267138006,\"y\":20.3},{\"x\":1685267438089,\"y\":20.3},{\"x\":1685267738177,\"y\":20.3},{\"x\":1685268038239,\"y\":20.4},{\"x\":1685268338288,\"y\":20.3},{\"x\":1685268638340,\"y\":20.4},{\"x\":1685268938413,\"y\":20.3},{\"x\":1685269238479,\"y\":20.4},{\"x\":1685269538567,\"y\":20.4},{\"x\":1685269838641,\"y\":20.4},{\"x\":1685270138681,\"y\":20.3},{\"x\":1685270438761,\"y\":20.3},{\"x\":1685270738813,\"y\":20.3},{\"x\":1685271038911,\"y\":20.3},{\"x\":1685271338993,\"y\":20},{\"x\":1685271638994,\"y\":20},{\"x\":1685271939107,\"y\":20.2},{\"x\":1685272239155,\"y\":20.3},{\"x\":1685272539230,\"y\":20.3},{\"x\":1685272839299,\"y\":20.3},{\"x\":1685273139339,\"y\":20.3},{\"x\":1685273439382,\"y\":20.2},{\"x\":1685273739450,\"y\":19.9},{\"x\":1685274039516,\"y\":19.9},{\"x\":1685274339570,\"y\":20.2},{\"x\":1685274639651,\"y\":20.3},{\"x\":1685274939715,\"y\":20.5},{\"x\":1685275239718,\"y\":20.5},{\"x\":1685275539821,\"y\":20.6},{\"x\":1685275839883,\"y\":20.5},{\"x\":1685276139936,\"y\":20.3},{\"x\":1685276439971,\"y\":20.3},{\"x\":1685276740019,\"y\":20.3},{\"x\":1685277040061,\"y\":20.2},{\"x\":1685277340154,\"y\":20},{\"x\":1685277640177,\"y\":19.9},{\"x\":1685277940231,\"y\":20},{\"x\":1685278240249,\"y\":20},{\"x\":1685278540299,\"y\":19.9},{\"x\":1685278840376,\"y\":19.9},{\"x\":1685279140399,\"y\":19.5},{\"x\":1685279440422,\"y\":19.7},{\"x\":1685279740489,\"y\":19.8},{\"x\":1685280040542,\"y\":19.8},{\"x\":1685280340547,\"y\":19.5},{\"x\":1685280640612,\"y\":19.5},{\"x\":1685280940692,\"y\":19.5},{\"x\":1685280995978,\"y\":19.5},{\"x\":1685281186232,\"y\":19.5},{\"x\":1685281372604,\"y\":19.5},{\"x\":1685281687601,\"y\":19.5},{\"x\":1685281710226,\"y\":19.5},{\"x\":1685281749591,\"y\":19.5},{\"x\":1685281762017,\"y\":19.5},{\"x\":1685281799605,\"y\":19.5},{\"x\":1685282023556,\"y\":19.5},{\"x\":1685282044430,\"y\":19.5},{\"x\":1685282166915,\"y\":19.5},{\"x\":1685282208131,\"y\":19.5},{\"x\":1685282217923,\"y\":19.5},{\"x\":1685282260379,\"y\":19.5},{\"x\":1685282339652,\"y\":19.5},{\"x\":1685282364558,\"y\":19.5},{\"x\":1685282412077,\"y\":19.5},{\"x\":1685282588188,\"y\":19.5},{\"x\":1685282614967,\"y\":19.5},{\"x\":1685282852605,\"y\":19.5},{\"x\":1685282960718,\"y\":19.5},{\"x\":1685282980565,\"y\":19.5},{\"x\":1685283037810,\"y\":19.5},{\"x\":1685283067001,\"y\":19.5},{\"x\":1685283122144,\"y\":19.5},{\"x\":1685283272482,\"y\":19.5},{\"x\":1685283343031,\"y\":19.5},{\"x\":1685283445211,\"y\":19.5},{\"x\":1685283473050,\"y\":19.5},{\"x\":1685283503945,\"y\":19.4},{\"x\":1685283559122,\"y\":19.5},{\"x\":1685283661335,\"y\":19.5},{\"x\":1685283679196,\"y\":19.5},{\"x\":1685283753141,\"y\":19.5},{\"x\":1685283789923,\"y\":19.4},{\"x\":1685283796674,\"y\":19.4},{\"x\":1685284111596,\"y\":19.5},{\"x\":1685284411624,\"y\":19.5},{\"x\":1685284726634,\"y\":20},{\"x\":1685285041633,\"y\":19.9},{\"x\":1685285341650,\"y\":19.6},{\"x\":1685285641691,\"y\":19.8},{\"x\":1685285941711,\"y\":20},{\"x\":1685286241759,\"y\":20},{\"x\":1685286541841,\"y\":19.7},{\"x\":1685286856821,\"y\":20},{\"x\":1685287156833,\"y\":20.3},{\"x\":1685287456865,\"y\":20.4},{\"x\":1685287756902,\"y\":20.3},{\"x\":1685288056933,\"y\":20.3},{\"x\":1685288357007,\"y\":20.3},{\"x\":1685288671988,\"y\":20.1},{\"x\":1685288972004,\"y\":20.2},{\"x\":1685289272052,\"y\":20},{\"x\":1685289572077,\"y\":20},{\"x\":1685289872089,\"y\":20},{\"x\":1685290172161,\"y\":19.9},{\"x\":1685290487147,\"y\":20},{\"x\":1685290787163,\"y\":20},{\"x\":1685291087218,\"y\":19.9},{\"x\":1685291387247,\"y\":19.9},{\"x\":1685291687274,\"y\":20},{\"x\":1685291987283,\"y\":20.2},{\"x\":1685292287334,\"y\":20},{\"x\":1685292587380,\"y\":19.9},{\"x\":1685292887389,\"y\":19.9},{\"x\":1685293187427,\"y\":19.8},{\"x\":1685293502456,\"y\":19.7},{\"x\":1685293817480,\"y\":19.5},{\"x\":1685294117492,\"y\":19.6},{\"x\":1685294417507,\"y\":19.9},{\"x\":1685294717548,\"y\":20},{\"x\":1685295017585,\"y\":19.9},{\"x\":1685295317622,\"y\":19.6},{\"x\":1685295617633,\"y\":20},{\"x\":1685295917642,\"y\":19.7},{\"x\":1685296217701,\"y\":19.9},{\"x\":1685296532704,\"y\":19.5},{\"x\":1685296832750,\"y\":19.5},{\"x\":1685297132754,\"y\":19.5},{\"x\":1685297432771,\"y\":19.5},{\"x\":1685297732796,\"y\":19.5},{\"x\":1685298032828,\"y\":19.5},{\"x\":1685298332882,\"y\":19.5},{\"x\":1685298647889,\"y\":19.5},{\"x\":1685298947921,\"y\":19.5},{\"x\":1685299247969,\"y\":19.5},{\"x\":1685299547997,\"y\":19.5},{\"x\":1685299848026,\"y\":19.5},{\"x\":1685300163050,\"y\":19.5},{\"x\":1685300463065,\"y\":19.5},{\"x\":1685300763075,\"y\":19.5},{\"x\":1685301063109,\"y\":19.5},{\"x\":1685301363164,\"y\":19.5},{\"x\":1685301678208,\"y\":19.5},{\"x\":1685301993216,\"y\":19.5},{\"x\":1685302293269,\"y\":19.5},{\"x\":1685302593314,\"y\":19.5},{\"x\":1685302908310,\"y\":19.5},{\"x\":1685303208329,\"y\":19.5},{\"x\":1685303508359,\"y\":19.5},{\"x\":1685303808369,\"y\":19.5},{\"x\":1685304108395,\"y\":19.5},{\"x\":1685304408424,\"y\":19.3},{\"x\":1685304708455,\"y\":19.4},{\"x\":1685305008461,\"y\":19.4},{\"x\":1685305308490,\"y\":19.4},{\"x\":1685305608530,\"y\":19.5},{\"x\":1685305908539,\"y\":19.5},{\"x\":1685306208562,\"y\":19.4},{\"x\":1685306508616,\"y\":19.5},{\"x\":1685306823616,\"y\":19.5},{\"x\":1685307123649,\"y\":19.5},{\"x\":1685307423661,\"y\":19.5},{\"x\":1685307723699,\"y\":19.5},{\"x\":1685308023733,\"y\":19.5},{\"x\":1685308323779,\"y\":19.5},{\"x\":1685308638772,\"y\":19.5},{\"x\":1685308938793,\"y\":19.5},{\"x\":1685309238818,\"y\":19.5},{\"x\":1685309538833,\"y\":19.5},{\"x\":1685309838872,\"y\":19.5},{\"x\":1685310138910,\"y\":19.5},{\"x\":1685310453907,\"y\":19.5},{\"x\":1685310768906,\"y\":19.5},{\"x\":1685311068915,\"y\":19.5},{\"x\":1685311368965,\"y\":19.5},{\"x\":1685311668966,\"y\":19.5},{\"x\":1685311968998,\"y\":19.5},{\"x\":1685312269016,\"y\":19.5},{\"x\":1685312569034,\"y\":19.5},{\"x\":1685312869061,\"y\":19.5},{\"x\":1685313169072,\"y\":19.5},{\"x\":1685313484093,\"y\":19.5},{\"x\":1685313784097,\"y\":19.6},{\"x\":1685314084113,\"y\":19.5},{\"x\":1685314384134,\"y\":19.7},{\"x\":1685314684165,\"y\":19.9},{\"x\":1685314984191,\"y\":19.9},{\"x\":1685315299157,\"y\":19.9},{\"x\":1685315599168,\"y\":20},{\"x\":1685315899192,\"y\":19.9},{\"x\":1685316199221,\"y\":20.1},{\"x\":1685316514243,\"y\":20},{\"x\":1685316814264,\"y\":20.2},{\"x\":1685317129218,\"y\":20.2},{\"x\":1685317444217,\"y\":20.2},{\"x\":1685317744298,\"y\":20.3},{\"x\":1685318059259,\"y\":20.3},{\"x\":1685318359275,\"y\":20.3},{\"x\":1685318659355,\"y\":20.3},{\"x\":1685318959404,\"y\":20.3},{\"x\":1685319259419,\"y\":20.2},{\"x\":1685319559443,\"y\":20.3},{\"x\":1685319859535,\"y\":20.3},{\"x\":1685320159597,\"y\":20.3},{\"x\":1685320459678,\"y\":20.3},{\"x\":1685320759851,\"y\":20.3},{\"x\":1685321074857,\"y\":20.2},{\"x\":1685321374890,\"y\":20.3},{\"x\":1685321674930,\"y\":20.3},{\"x\":1685321974980,\"y\":20.3},{\"x\":1685322274993,\"y\":20.3},{\"x\":1685322575024,\"y\":20.3},{\"x\":1685322875122,\"y\":20.3},{\"x\":1685323175196,\"y\":20.3},{\"x\":1685323475244,\"y\":20.3},{\"x\":1685323775309,\"y\":20.4},{\"x\":1685324075375,\"y\":20.3},{\"x\":1685324375402,\"y\":20.4},{\"x\":1685324675425,\"y\":20.5},{\"x\":1685324975485,\"y\":20.5},{\"x\":1685325275523,\"y\":20.5},{\"x\":1685325575582,\"y\":20.5},{\"x\":1685325875647,\"y\":20.5},{\"x\":1685326175713,\"y\":20.5},{\"x\":1685326475768,\"y\":20.4},{\"x\":1685326775825,\"y\":20.4},{\"x\":1685327075908,\"y\":20.4},{\"x\":1685327376002,\"y\":20.4},{\"x\":1685327676015,\"y\":20.4},{\"x\":1685327976059,\"y\":20.4},{\"x\":1685328276184,\"y\":20.4},{\"x\":1685328591156,\"y\":20.4},{\"x\":1685328891332,\"y\":20.4},{\"x\":1685329206306,\"y\":20.4},{\"x\":1685329506397,\"y\":20.4},{\"x\":1685329806440,\"y\":20.3},{\"x\":1685330106549,\"y\":20.3},{\"x\":1685330406587,\"y\":20.3},{\"x\":1685330706687,\"y\":20.2},{\"x\":1685331006741,\"y\":20.3},{\"x\":1685331306759,\"y\":20.3},{\"x\":1685331606822,\"y\":20.2},{\"x\":1685331906872,\"y\":20.2},{\"x\":1685332206974,\"y\":20.2},{\"x\":1685332507043,\"y\":20.3},{\"x\":1685332807216,\"y\":20.2},{\"x\":1685333122210,\"y\":20.2},{\"x\":1685333422298,\"y\":20.2},{\"x\":1685333722396,\"y\":20.2},{\"x\":1685334022464,\"y\":20.2},{\"x\":1685334322536,\"y\":20.2},{\"x\":1685334622591,\"y\":20.2},{\"x\":1685334922644,\"y\":20.2},{\"x\":1685335222714,\"y\":20.1},{\"x\":1685335522841,\"y\":20},{\"x\":1685335822919,\"y\":20},{\"x\":1685336122954,\"y\":20},{\"x\":1685336423087,\"y\":19.9},{\"x\":1685336723138,\"y\":20},{\"x\":1685337023185,\"y\":19.9},{\"x\":1685337323274,\"y\":19.9},{\"x\":1685337623330,\"y\":19.9},{\"x\":1685337923414,\"y\":20},{\"x\":1685338223520,\"y\":19.9},{\"x\":1685338523616,\"y\":20},{\"x\":1685338823724,\"y\":19.9},{\"x\":1685339123797,\"y\":20},{\"x\":1685339423870,\"y\":19.9},{\"x\":1685339723950,\"y\":19.9},{\"x\":1685340024030,\"y\":19.9},{\"x\":1685340324090,\"y\":20},{\"x\":1685340624115,\"y\":20},{\"x\":1685340924164,\"y\":20},{\"x\":1685341224265,\"y\":20},{\"x\":1685341524353,\"y\":20},{\"x\":1685341824453,\"y\":20},{\"x\":1685342124531,\"y\":20.2},{\"x\":1685342424622,\"y\":19.9},{\"x\":1685342724753,\"y\":20},{\"x\":1685343024799,\"y\":20.2},{\"x\":1685343324830,\"y\":20},{\"x\":1685343624890,\"y\":19.9},{\"x\":1685343924971,\"y\":20.2},{\"x\":1685344225070,\"y\":20.2},{\"x\":1685344525149,\"y\":20.3},{\"x\":1685344825216,\"y\":20.2},{\"x\":1685345125344,\"y\":20.2},{\"x\":1685345425362,\"y\":20.3},{\"x\":1685345725441,\"y\":20.3},{\"x\":1685346025536,\"y\":20.3},{\"x\":1685346325591,\"y\":20},{\"x\":1685346625679,\"y\":20.2},{\"x\":1685346925731,\"y\":20.2},{\"x\":1685347225748,\"y\":20.3},{\"x\":1685347525839,\"y\":20.3},{\"x\":1685347825931,\"y\":20.3},{\"x\":1685348126031,\"y\":20.3},{\"x\":1685348426089,\"y\":20.3},{\"x\":1685348726163,\"y\":20.3},{\"x\":1685349026225,\"y\":20.3},{\"x\":1685349326308,\"y\":20.3},{\"x\":1685349626392,\"y\":20},{\"x\":1685349926466,\"y\":20},{\"x\":1685350226553,\"y\":20.2},{\"x\":1685350526651,\"y\":19.9},{\"x\":1685350826811,\"y\":20},{\"x\":1685351141763,\"y\":19.9},{\"x\":1685351441827,\"y\":20.2},{\"x\":1685351741927,\"y\":20.2},{\"x\":1685352042064,\"y\":20.1},{\"x\":1685352342100,\"y\":20.3},{\"x\":1685352642170,\"y\":20},{\"x\":1685352942245,\"y\":19.9},{\"x\":1685353242314,\"y\":19.9},{\"x\":1685353542387,\"y\":20},{\"x\":1685353842461,\"y\":20},{\"x\":1685354142491,\"y\":20},{\"x\":1685354442557,\"y\":19.9},{\"x\":1685354742628,\"y\":20},{\"x\":1685355042729,\"y\":20.3},{\"x\":1685355342838,\"y\":20.2},{\"x\":1685355642921,\"y\":20.3},{\"x\":1685355942977,\"y\":20},{\"x\":1685356243149,\"y\":19.9},{\"x\":1685356558148,\"y\":19.5},{\"x\":1685356858211,\"y\":19.9},{\"x\":1685357158249,\"y\":20},{\"x\":1685357458282,\"y\":19.6},{\"x\":1685357758424,\"y\":20},{\"x\":1685358058471,\"y\":19.5},{\"x\":1685358358569,\"y\":19.5},{\"x\":1685358658635,\"y\":20},{\"x\":1685358958716,\"y\":20.1},{\"x\":1685359258767,\"y\":20},{\"x\":1685359558873,\"y\":19.9},{\"x\":1685359858954,\"y\":20.2},{\"x\":1685360159014,\"y\":20.2},{\"x\":1685360459077,\"y\":20},{\"x\":1685360759117,\"y\":19.9},{\"x\":1685361059194,\"y\":19.8},{\"x\":1685361359285,\"y\":20},{\"x\":1685361659377,\"y\":20},{\"x\":1685361959454,\"y\":20.1},{\"x\":1685362259508,\"y\":20.2},{\"x\":1685362559586,\"y\":19.9},{\"x\":1685362859674,\"y\":20},{\"x\":1685363159756,\"y\":20},{\"x\":1685363459810,\"y\":19.5},{\"x\":1685363759869,\"y\":19.6},{\"x\":1685364059953,\"y\":19.5},{\"x\":1685364360040,\"y\":19.9},{\"x\":1685364660157,\"y\":19.9},{\"x\":1685364960261,\"y\":19.9},{\"x\":1685365260272,\"y\":19.9},{\"x\":1685365560367,\"y\":20},{\"x\":1685365860483,\"y\":19.9},{\"x\":1685366160569,\"y\":19.9},{\"x\":1685366460633,\"y\":19.5},{\"x\":1685366760670,\"y\":19.5},{\"x\":1685367060781,\"y\":19.5},{\"x\":1685367360928,\"y\":19.5},{\"x\":1685367660996,\"y\":19.5},{\"x\":1685367961125,\"y\":19.5},{\"x\":1685368261267,\"y\":19.5},{\"x\":1685368561326,\"y\":19.5},{\"x\":1685368861443,\"y\":19.5},{\"x\":1685369161528,\"y\":19.5},{\"x\":1685369461571,\"y\":19.5},{\"x\":1685369761618,\"y\":19.5},{\"x\":1685370061811,\"y\":19.5},{\"x\":1685370361876,\"y\":19.5},{\"x\":1685370661975,\"y\":19.5},{\"x\":1685370962096,\"y\":19.5},{\"x\":1685371262161,\"y\":19.5},{\"x\":1685371562287,\"y\":19.5},{\"x\":1685371862386,\"y\":19.5},{\"x\":1685372162471,\"y\":19.5},{\"x\":1685372462563,\"y\":19.4},{\"x\":1685372762583,\"y\":19.2},{\"x\":1685373062692,\"y\":19.2},{\"x\":1685373362813,\"y\":19.1},{\"x\":1685373662925,\"y\":19.3},{\"x\":1685373963012,\"y\":19.2},{\"x\":1685374263073,\"y\":19.2},{\"x\":1685374563193,\"y\":19},{\"x\":1685374863306,\"y\":19.2},{\"x\":1685375163412,\"y\":19.3},{\"x\":1685375463446,\"y\":19.4},{\"x\":1685375763533,\"y\":19.4},{\"x\":1685376063611,\"y\":19.2},{\"x\":1685376363690,\"y\":19.1},{\"x\":1685376663795,\"y\":19},{\"x\":1685376963909,\"y\":19.2},{\"x\":1685377263998,\"y\":19.3},{\"x\":1685377564112,\"y\":19.1},{\"x\":1685377864184,\"y\":19.2},{\"x\":1685378164260,\"y\":19.1},{\"x\":1685378464309,\"y\":19},{\"x\":1685378764426,\"y\":19},{\"x\":1685379064528,\"y\":19},{\"x\":1685379364568,\"y\":19.2},{\"x\":1685379664627,\"y\":19.1},{\"x\":1685379964711,\"y\":19},{\"x\":1685380264778,\"y\":19.1},{\"x\":1685380564838,\"y\":19.2},{\"x\":1685380864899,\"y\":19},{\"x\":1685381164922,\"y\":19},{\"x\":1685381464995,\"y\":18.9},{\"x\":1685381765085,\"y\":18.9},{\"x\":1685382065145,\"y\":18.9},{\"x\":1685382365238,\"y\":18.9},{\"x\":1685382665304,\"y\":18.9},{\"x\":1685382965394,\"y\":19},{\"x\":1685383265458,\"y\":19},{\"x\":1685383565523,\"y\":18.9},{\"x\":1685383865573,\"y\":18.9},{\"x\":1685384165635,\"y\":18.7},{\"x\":1685384465670,\"y\":18.7},{\"x\":1685384765740,\"y\":18.7},{\"x\":1685385065812,\"y\":18.9},{\"x\":1685385365909,\"y\":18.8},{\"x\":1685385665940,\"y\":18.7},{\"x\":1685385966015,\"y\":18.9},{\"x\":1685386266095,\"y\":18.9},{\"x\":1685386566159,\"y\":18.8},{\"x\":1685386866204,\"y\":18.5},{\"x\":1685387166270,\"y\":18.5},{\"x\":1685387466325,\"y\":18.7},{\"x\":1685387766371,\"y\":18.5},{\"x\":1685388066417,\"y\":18.7},{\"x\":1685388366441,\"y\":18.7},{\"x\":1685388666495,\"y\":18.7},{\"x\":1685388966561,\"y\":18.4},{\"x\":1685389266600,\"y\":18.4},{\"x\":1685389566607,\"y\":18.4},{\"x\":1685389866660,\"y\":18.4},{\"x\":1685390166677,\"y\":18.5},{\"x\":1685390466746,\"y\":18.5},{\"x\":1685390781720,\"y\":18.6},{\"x\":1685391081770,\"y\":18.6},{\"x\":1685391381808,\"y\":18.4},{\"x\":1685391681836,\"y\":18.4},{\"x\":1685391981922,\"y\":18.4},{\"x\":1685392281964,\"y\":18.4},{\"x\":1685392582032,\"y\":18.4},{\"x\":1685392897029,\"y\":18.4},{\"x\":1685393197037,\"y\":18.4},{\"x\":1685393497068,\"y\":18.4},{\"x\":1685393797107,\"y\":18.4},{\"x\":1685394097146,\"y\":18.4},{\"x\":1685394397195,\"y\":18.4},{\"x\":1685394697240,\"y\":18.4},{\"x\":1685394997269,\"y\":18.4},{\"x\":1685395297315,\"y\":18.4},{\"x\":1685395597344,\"y\":18.4},{\"x\":1685395897378,\"y\":18.4},{\"x\":1685396197391,\"y\":18.4},{\"x\":1685396497411,\"y\":18.4},{\"x\":1685396797418,\"y\":18.3},{\"x\":1685397097514,\"y\":18.4},{\"x\":1685397397526,\"y\":18.2},{\"x\":1685397697553,\"y\":18.1},{\"x\":1685397997603,\"y\":18.1},{\"x\":1685398297622,\"y\":18.1},{\"x\":1685398597700,\"y\":18.1},{\"x\":1685398897737,\"y\":18.2},{\"x\":1685399212765,\"y\":18.2},{\"x\":1685399527768,\"y\":18},{\"x\":1685399827776,\"y\":18.1},{\"x\":1685400127841,\"y\":17.9},{\"x\":1685400442841,\"y\":18},{\"x\":1685400742935,\"y\":17.9},{\"x\":1685401057950,\"y\":18},{\"x\":1685401357960,\"y\":18},{\"x\":1685401657984,\"y\":18.1},{\"x\":1685401957995,\"y\":18},{\"x\":1685402258067,\"y\":18.1},{\"x\":1685402573081,\"y\":18},{\"x\":1685402873096,\"y\":17.9},{\"x\":1685403173117,\"y\":17.9},{\"x\":1685403473129,\"y\":17.9},{\"x\":1685403773148,\"y\":17.9},{\"x\":1685404073175,\"y\":18},{\"x\":1685404373229,\"y\":17.9},{\"x\":1685404688249,\"y\":17.9},{\"x\":1685404988317,\"y\":17.9},{\"x\":1685405288409,\"y\":17.9},{\"x\":1685405588488,\"y\":17.9},{\"x\":1685405888522,\"y\":17.9},{\"x\":1685406188569,\"y\":17.9},{\"x\":1685406488623,\"y\":17.9},{\"x\":1685406788688,\"y\":17.9},{\"x\":1685407088721,\"y\":17.9},{\"x\":1685407388765,\"y\":17.9},{\"x\":1685407688838,\"y\":17.9},{\"x\":1685407988885,\"y\":17.9},{\"x\":1685408288915,\"y\":17.9},{\"x\":1685408588993,\"y\":17.9},{\"x\":1685408889074,\"y\":17.9},{\"x\":1685409189147,\"y\":17.9},{\"x\":1685409489206,\"y\":17.9},{\"x\":1685409789249,\"y\":17.9},{\"x\":1685410089310,\"y\":17.9},{\"x\":1685410389345,\"y\":17.9},{\"x\":1685410704354,\"y\":17.9},{\"x\":1685411004452,\"y\":17.9},{\"x\":1685411304516,\"y\":17.9},{\"x\":1685411604552,\"y\":17.9},{\"x\":1685411904625,\"y\":17.8},{\"x\":1685412204887,\"y\":17.8},{\"x\":1685412519762,\"y\":17.7},{\"x\":1685412819867,\"y\":17.7},{\"x\":1685413119886,\"y\":17.8},{\"x\":1685413419976,\"y\":17.7},{\"x\":1685413719986,\"y\":17.7},{\"x\":1685414020028,\"y\":17.7},{\"x\":1685414320085,\"y\":17.7},{\"x\":1685414620140,\"y\":17.7},{\"x\":1685414920229,\"y\":17.7},{\"x\":1685415220270,\"y\":17.7},{\"x\":1685415520407,\"y\":17.7},{\"x\":1685415820425,\"y\":17.7},{\"x\":1685416120509,\"y\":17.7},{\"x\":1685416420519,\"y\":17.7},{\"x\":1685416720605,\"y\":17.7},{\"x\":1685417020626,\"y\":17.7},{\"x\":1685417320672,\"y\":17.7},{\"x\":1685417620774,\"y\":17.7},{\"x\":1685417920843,\"y\":17.7},{\"x\":1685418220900,\"y\":17.8},{\"x\":1685418520971,\"y\":17.7},{\"x\":1685418821022,\"y\":17.7},{\"x\":1685419121073,\"y\":17.8},{\"x\":1685419421171,\"y\":17.9},{\"x\":1685419721181,\"y\":17.7},{\"x\":1685420021225,\"y\":17.7},{\"x\":1685420321304,\"y\":17.6},{\"x\":1685420621355,\"y\":17.7},{\"x\":1685420921447,\"y\":17.7},{\"x\":1685421221522,\"y\":17.7},{\"x\":1685421521582,\"y\":17.7},{\"x\":1685421821663,\"y\":17.7},{\"x\":1685422121736,\"y\":17.6},{\"x\":1685422421818,\"y\":17.7},{\"x\":1685422721865,\"y\":17.7},{\"x\":1685423021869,\"y\":17.6},{\"x\":1685423321956,\"y\":17.4},{\"x\":1685423622015,\"y\":17.5},{\"x\":1685423922080,\"y\":17.4},{\"x\":1685424222162,\"y\":17.5},{\"x\":1685424522203,\"y\":17.5},{\"x\":1685424822288,\"y\":17.5},{\"x\":1685425122362,\"y\":17.4},{\"x\":1685425422438,\"y\":17.4},{\"x\":1685425722513,\"y\":17.4},{\"x\":1685426022547,\"y\":17.4},{\"x\":1685426322620,\"y\":17.5},{\"x\":1685426622723,\"y\":17.5},{\"x\":1685426922744,\"y\":17.4},{\"x\":1685427222854,\"y\":17.4},{\"x\":1685427522913,\"y\":17.4},{\"x\":1685427823002,\"y\":17.4},{\"x\":1685428123104,\"y\":17.5},{\"x\":1685428423173,\"y\":17.5},{\"x\":1685428723329,\"y\":17.4},{\"x\":1685429038338,\"y\":17.4},{\"x\":1685429338390,\"y\":17.4},{\"x\":1685429638534,\"y\":17.4},{\"x\":1685429938550,\"y\":17.4},{\"x\":1685430238655,\"y\":17.4},{\"x\":1685430538750,\"y\":17.4},{\"x\":1685430838822,\"y\":17.3},{\"x\":1685431139008,\"y\":17.5},{\"x\":1685431439050,\"y\":17.5},{\"x\":1685431739109,\"y\":17.4},{\"x\":1685432039156,\"y\":17.5},{\"x\":1685432339204,\"y\":17.7},{\"x\":1685432639299,\"y\":17.3},{\"x\":1685432939394,\"y\":17.2},{\"x\":1685433239499,\"y\":17.2},{\"x\":1685433539575,\"y\":17.2},{\"x\":1685433839700,\"y\":17.2},{\"x\":1685434139771,\"y\":16.9},{\"x\":1685434439873,\"y\":16.7},{\"x\":1685434739965,\"y\":16.5},{\"x\":1685435040038,\"y\":16.5},{\"x\":1685435340054,\"y\":16.5},{\"x\":1685435640133,\"y\":16.5},{\"x\":1685435940205,\"y\":16.5},{\"x\":1685436240311,\"y\":16.8},{\"x\":1685436540423,\"y\":16.9},{\"x\":1685436840548,\"y\":17.1},{\"x\":1685437140665,\"y\":17},{\"x\":1685437440701,\"y\":16.8},{\"x\":1685437740799,\"y\":16.5},{\"x\":1685438040887,\"y\":16.5},{\"x\":1685438340964,\"y\":16.5},{\"x\":1685438641030,\"y\":17.2},{\"x\":1685438941063,\"y\":17.2},{\"x\":1685439241150,\"y\":17.2},{\"x\":1685439541265,\"y\":17},{\"x\":1685439841366,\"y\":17.2},{\"x\":1685440141451,\"y\":16.5},{\"x\":1685440441506,\"y\":16.5},{\"x\":1685440741607,\"y\":16.5},{\"x\":1685441041684,\"y\":16.5},{\"x\":1685441341786,\"y\":16.5},{\"x\":1685441641830,\"y\":16.5},{\"x\":1685441941863,\"y\":16.5},{\"x\":1685442241939,\"y\":17},{\"x\":1685442542010,\"y\":17.3},{\"x\":1685442842108,\"y\":17.2},{\"x\":1685443142199,\"y\":16.6},{\"x\":1685443442319,\"y\":17.2},{\"x\":1685443742376,\"y\":16.5},{\"x\":1685444042480,\"y\":16.8},{\"x\":1685444342574,\"y\":16.8},{\"x\":1685444642649,\"y\":16.5},{\"x\":1685444942748,\"y\":17.2},{\"x\":1685445242792,\"y\":17.1},{\"x\":1685445542799,\"y\":17.2},{\"x\":1685445842907,\"y\":17.3},{\"x\":1685446143003,\"y\":17.4},{\"x\":1685446443122,\"y\":17.4},{\"x\":1685446743194,\"y\":17.3},{\"x\":1685447043298,\"y\":17.2},{\"x\":1685447343341,\"y\":17.4},{\"x\":1685447643442,\"y\":17.2},{\"x\":1685447943557,\"y\":17.2},{\"x\":1685448243632,\"y\":17.3},{\"x\":1685448543708,\"y\":17.2},{\"x\":1685448843747,\"y\":17.1},{\"x\":1685449143768,\"y\":16.6},{\"x\":1685449443878,\"y\":16.5},{\"x\":1685449743965,\"y\":16.5},{\"x\":1685450044053,\"y\":16.5},{\"x\":1685450344178,\"y\":17.2},{\"x\":1685450644263,\"y\":17.3},{\"x\":1685450944279,\"y\":17.4},{\"x\":1685451244382,\"y\":17.2},{\"x\":1685451544448,\"y\":17.3},{\"x\":1685451844600,\"y\":17.3},{\"x\":1685452144626,\"y\":17.4},{\"x\":1685452444701,\"y\":17.4},{\"x\":1685452744749,\"y\":17.2},{\"x\":1685453044818,\"y\":17.4},{\"x\":1685453344909,\"y\":17.7},{\"x\":1685453645071,\"y\":17.6},{\"x\":1685453945113,\"y\":17.5},{\"x\":1685454245205,\"y\":17.6},{\"x\":1685454545276,\"y\":17.6},{\"x\":1685454845390,\"y\":17.7},{\"x\":1685455145455,\"y\":17.8},{\"x\":1685455445552,\"y\":17.7},{\"x\":1685455745631,\"y\":17.7},{\"x\":1685456045706,\"y\":17.7},{\"x\":1685456345813,\"y\":17.6},{\"x\":1685456645826,\"y\":17.7},{\"x\":1685456945928,\"y\":17.7},{\"x\":1685457246034,\"y\":17.4},{\"x\":1685457546104,\"y\":17.4},{\"x\":1685457846188,\"y\":17.6},{\"x\":1685458146263,\"y\":17.9},{\"x\":1685458446341,\"y\":17.9},{\"x\":1685458746470,\"y\":17.9},{\"x\":1685459046554,\"y\":17.8},{\"x\":1685459346643,\"y\":17.7},{\"x\":1685459646708,\"y\":17.9},{\"x\":1685459946734,\"y\":18},{\"x\":1685460246833,\"y\":17.9},{\"x\":1685460546913,\"y\":17.9},{\"x\":1685460847002,\"y\":17.9},{\"x\":1685461147079,\"y\":17.9},{\"x\":1685461447115,\"y\":17.9},{\"x\":1685461747181,\"y\":17.9},{\"x\":1685462047303,\"y\":17.9},{\"x\":1685462347360,\"y\":17.9},{\"x\":1685462647457,\"y\":17.9},{\"x\":1685462947533,\"y\":17.9},{\"x\":1685463247622,\"y\":17.9},{\"x\":1685463437946,\"y\":17.9},{\"x\":1685463752894,\"y\":17.9},{\"x\":1685464067891,\"y\":17.9},{\"x\":1685464367932,\"y\":17.9},{\"x\":1685464668016,\"y\":18.2},{\"x\":1685464968034,\"y\":18.2},{\"x\":1685465268109,\"y\":17.9},{\"x\":1685465568127,\"y\":17.9},{\"x\":1685465868179,\"y\":18},{\"x\":1685466168200,\"y\":18},{\"x\":1685466468292,\"y\":17.9},{\"x\":1685466783247,\"y\":17.9},{\"x\":1685467083281,\"y\":18},{\"x\":1685467383324,\"y\":18},{\"x\":1685467683356,\"y\":17.9},{\"x\":1685467983393,\"y\":17.9},{\"x\":1685468283508,\"y\":18},{\"x\":1685468598507,\"y\":17.9},{\"x\":1685468898532,\"y\":17.9},{\"x\":1685469198568,\"y\":17.9},{\"x\":1685469498602,\"y\":17.9},{\"x\":1685469798635,\"y\":17.9},{\"x\":1685470098653,\"y\":17.8},{\"x\":1685470398701,\"y\":17.8},{\"x\":1685470698741,\"y\":17.8},{\"x\":1685470998770,\"y\":17.7},{\"x\":1685471298800,\"y\":17.8},{\"x\":1685471598821,\"y\":17.9},{\"x\":1685471898877,\"y\":17.7},{\"x\":1685472198886,\"y\":17.7},{\"x\":1685472498957,\"y\":17.7},{\"x\":1685472798992,\"y\":17.7},{\"x\":1685473099049,\"y\":17.9},{\"x\":1685473414059,\"y\":17.9},{\"x\":1685473714086,\"y\":17.7},{\"x\":1685474014164,\"y\":17.6},{\"x\":1685474314238,\"y\":17.4},{\"x\":1685474629191,\"y\":17.5},{\"x\":1685474929212,\"y\":17.6},{\"x\":1685475229261,\"y\":17.7},{\"x\":1685475529275,\"y\":17.4},{\"x\":1685475829340,\"y\":17.4},{\"x\":1685476129417,\"y\":17.4},{\"x\":1685476429433,\"y\":17.4},{\"x\":1685476729449,\"y\":17.3},{\"x\":1685477029470,\"y\":17.2},{\"x\":1685477329512,\"y\":17.2},{\"x\":1685477629544,\"y\":17.3},{\"x\":1685477929562,\"y\":17.4},{\"x\":1685478229615,\"y\":17.3},{\"x\":1685478529641,\"y\":17},{\"x\":1685478829667,\"y\":16.5},{\"x\":1685479129749,\"y\":16.5},{\"x\":1685479429791,\"y\":16.5},{\"x\":1685479729807,\"y\":16.5},{\"x\":1685480029843,\"y\":16.5},{\"x\":1685480329850,\"y\":16.5},{\"x\":1685480629899,\"y\":16.7},{\"x\":1685480929954,\"y\":16.5},{\"x\":1685481244998,\"y\":16.5},{\"x\":1685481545012,\"y\":16.5},{\"x\":1685481845043,\"y\":16.5},{\"x\":1685482145093,\"y\":16.5},{\"x\":1685482445120,\"y\":16.5},{\"x\":1685482745153,\"y\":16.5},{\"x\":1685483045222,\"y\":16.5},{\"x\":1685483345235,\"y\":16.5},{\"x\":1685483645258,\"y\":16.5},{\"x\":1685483945299,\"y\":16.5},{\"x\":1685484245340,\"y\":16.5},{\"x\":1685484545382,\"y\":16.5},{\"x\":1685484845385,\"y\":16.5},{\"x\":1685485145425,\"y\":16.5},{\"x\":1685485445459,\"y\":16.5},{\"x\":1685485745479,\"y\":16.5},{\"x\":1685486045546,\"y\":16.5},{\"x\":1685486345575,\"y\":16.5},{\"x\":1685486645633,\"y\":16.5},{\"x\":1685486945725,\"y\":16.5},{\"x\":1685487260693,\"y\":16.5},{\"x\":1685487560707,\"y\":16.5},{\"x\":1685487860814,\"y\":16.5},{\"x\":1685488175808,\"y\":16.5},{\"x\":1685488475825,\"y\":16.5},{\"x\":1685488775853,\"y\":16.5},{\"x\":1685489075914,\"y\":16.5},{\"x\":1685489375930,\"y\":16.5},{\"x\":1685489675982,\"y\":16.5},{\"x\":1685489976039,\"y\":16.5},{\"x\":1685490276048,\"y\":16.5},{\"x\":1685490576126,\"y\":16.5},{\"x\":1685490876150,\"y\":16.5},{\"x\":1685491176154,\"y\":16.5},{\"x\":1685491476262,\"y\":16.5},{\"x\":1685491791218,\"y\":16.5},{\"x\":1685492091237,\"y\":16.5},{\"x\":1685492391276,\"y\":16.5},{\"x\":1685492691341,\"y\":16.5},{\"x\":1685492991352,\"y\":16.5},{\"x\":1685493291416,\"y\":16.5},{\"x\":1685493591464,\"y\":16.5},{\"x\":1685493891476,\"y\":16.2},{\"x\":1685494191553,\"y\":16.5},{\"x\":1685494491555,\"y\":16.5},{\"x\":1685494806655,\"y\":16.5},{\"x\":1685495121636,\"y\":16.5},{\"x\":1685495436647,\"y\":16.5},{\"x\":1685495736703,\"y\":16.5},{\"x\":1685496036726,\"y\":16.4},{\"x\":1685496336779,\"y\":16.5},{\"x\":1685496636854,\"y\":16.4},{\"x\":1685496951873,\"y\":16.4},{\"x\":1685497251911,\"y\":16.5},{\"x\":1685497566911,\"y\":16.5},{\"x\":1685497866915,\"y\":16.5},{\"x\":1685498166972,\"y\":16.2},{\"x\":1685498466989,\"y\":16.2},{\"x\":1685498767043,\"y\":16.4},{\"x\":1685499067095,\"y\":16.2},{\"x\":1685499367129,\"y\":16.5},{\"x\":1685499667180,\"y\":16.4},{\"x\":1685499967184,\"y\":16.5},{\"x\":1685500282215,\"y\":16.5},{\"x\":1685500582236,\"y\":16.5},{\"x\":1685500882309,\"y\":16.5},{\"x\":1685501197336,\"y\":16.4},{\"x\":1685501497371,\"y\":16.2},{\"x\":1685501797405,\"y\":16.3},{\"x\":1685502097446,\"y\":16.1},{\"x\":1685502397471,\"y\":16},{\"x\":1685502697518,\"y\":15.7},{\"x\":1685502997526,\"y\":16},{\"x\":1685503297549,\"y\":16},{\"x\":1685503597589,\"y\":16.2},{\"x\":1685503897624,\"y\":16.2},{\"x\":1685504197687,\"y\":16},{\"x\":1685504497728,\"y\":15.9},{\"x\":1685504797753,\"y\":16},{\"x\":1685505097809,\"y\":16},{\"x\":1685505397837,\"y\":16.2},{\"x\":1685505697885,\"y\":16.4},{\"x\":1685505997899,\"y\":16.3},{\"x\":1685506297916,\"y\":16.1},{\"x\":1685506597958,\"y\":16},{\"x\":1685506897987,\"y\":16},{\"x\":1685507198004,\"y\":15.8},{\"x\":1685507498078,\"y\":15.9},{\"x\":1685507798110,\"y\":16},{\"x\":1685508098131,\"y\":15.8},{\"x\":1685508398152,\"y\":16},{\"x\":1685508698194,\"y\":15.9},{\"x\":1685508998213,\"y\":15.8},{\"x\":1685509298220,\"y\":15.8},{\"x\":1685509598290,\"y\":15.9},{\"x\":1685509898323,\"y\":15.7},{\"x\":1685510198331,\"y\":15.7},{\"x\":1685510498370,\"y\":15.7},{\"x\":1685510798393,\"y\":15.5},{\"x\":1685511098454,\"y\":15.8},{\"x\":1685511398491,\"y\":15.7},{\"x\":1685511698520,\"y\":15.9},{\"x\":1685512013528,\"y\":15.9},{\"x\":1685512313605,\"y\":15.8},{\"x\":1685512628564,\"y\":15.7},{\"x\":1685512928630,\"y\":15.5},{\"x\":1685513228650,\"y\":15.5},{\"x\":1685513528663,\"y\":15.5},{\"x\":1685513828703,\"y\":15.6},{\"x\":1685514143711,\"y\":15.7},{\"x\":1685514443729,\"y\":15.7},{\"x\":1685514743749,\"y\":15.8},{\"x\":1685515043937,\"y\":15.7},{\"x\":1685515358896,\"y\":15.5},{\"x\":1685515658956,\"y\":15.6},{\"x\":1685515959007,\"y\":15.7},{\"x\":1685516259087,\"y\":15.6},{\"x\":1685516559112,\"y\":15.9},{\"x\":1685516859194,\"y\":16},{\"x\":1685517174175,\"y\":15.9},{\"x\":1685517474229,\"y\":15.8},{\"x\":1685517774323,\"y\":15.7},{\"x\":1685518074366,\"y\":15.7},{\"x\":1685518374438,\"y\":15.7},{\"x\":1685518674561,\"y\":15.9},{\"x\":1685518974586,\"y\":15.7},{\"x\":1685519274602,\"y\":16},{\"x\":1685519574700,\"y\":15.7},{\"x\":1685519889719,\"y\":15.8},{\"x\":1685520189753,\"y\":15.9},{\"x\":1685520489831,\"y\":15.8},{\"x\":1685520789861,\"y\":15.9},{\"x\":1685521089913,\"y\":16},{\"x\":1685521390016,\"y\":15.7},{\"x\":1685521690069,\"y\":15.5},{\"x\":1685521990135,\"y\":15.5},{\"x\":1685522290216,\"y\":15.5},{\"x\":1685522590301,\"y\":15.5},{\"x\":1685522905260,\"y\":15.7},{\"x\":1685523205311,\"y\":15.7},{\"x\":1685523505402,\"y\":15.7},{\"x\":1685523805456,\"y\":15.6},{\"x\":1685524105543,\"y\":15.9},{\"x\":1685524405592,\"y\":15.7},{\"x\":1685524705754,\"y\":15.5},{\"x\":1685525005757,\"y\":15.5},{\"x\":1685525305830,\"y\":15.7},{\"x\":1685525605894,\"y\":15.5},{\"x\":1685525905934,\"y\":15.7},{\"x\":1685526205944,\"y\":15.7},{\"x\":1685526505986,\"y\":15.5},{\"x\":1685526806078,\"y\":15.6},{\"x\":1685527106180,\"y\":15.8},{\"x\":1685527406287,\"y\":15.5},{\"x\":1685527706314,\"y\":15.6},{\"x\":1685528006417,\"y\":15.7},{\"x\":1685528306469,\"y\":15.8},{\"x\":1685528606541,\"y\":15.9},{\"x\":1685528906576,\"y\":15.7},{\"x\":1685529206598,\"y\":15.7},{\"x\":1685529506705,\"y\":15.7},{\"x\":1685529806799,\"y\":15.5},{\"x\":1685530106900,\"y\":15.7},{\"x\":1685530406997,\"y\":15.9},{\"x\":1685530707031,\"y\":15.9},{\"x\":1685531007135,\"y\":15.7},{\"x\":1685531307217,\"y\":15.9},{\"x\":1685531607277,\"y\":15.7},{\"x\":1685531907312,\"y\":15.9},{\"x\":1685532207333,\"y\":16},{\"x\":1685532507449,\"y\":15.9},{\"x\":1685532807538,\"y\":16},{\"x\":1685533107607,\"y\":15.9},{\"x\":1685533407716,\"y\":16.1},{\"x\":1685533707801,\"y\":16.4},{\"x\":1685534007891,\"y\":16.5},{\"x\":1685534307936,\"y\":17.9},{\"x\":1685534607976,\"y\":18.5},{\"x\":1685534908043,\"y\":19},{\"x\":1685535208090,\"y\":19.4},{\"x\":1685535508186,\"y\":19.5},{\"x\":1685535808331,\"y\":19.5},{\"x\":1685536108352,\"y\":19.5},{\"x\":1685536408438,\"y\":19.9},{\"x\":1685536708630,\"y\":20.2},{\"x\":1685537023666,\"y\":20.3},{\"x\":1685537323705,\"y\":20.5},{\"x\":1685537623753,\"y\":20.4},{\"x\":1685537923799,\"y\":20.4},{\"x\":1685538223965,\"y\":20.5},{\"x\":1685538538965,\"y\":20.4},{\"x\":1685538839060,\"y\":20.5},{\"x\":1685539139180,\"y\":20.5},{\"x\":1685539439233,\"y\":20.5},{\"x\":1685539739375,\"y\":20.4},{\"x\":1685540039424,\"y\":20.6},{\"x\":1685540339520,\"y\":20.4},{\"x\":1685540639601,\"y\":20.6},{\"x\":1685540939668,\"y\":20.5},{\"x\":1685541239749,\"y\":20.5},{\"x\":1685541539840,\"y\":20.8},{\"x\":1685541839913,\"y\":20.5},{\"x\":1685542139995,\"y\":20.4},{\"x\":1685542440177,\"y\":20.8},{\"x\":1685542740192,\"y\":20.4},{\"x\":1685543040283,\"y\":20.8},{\"x\":1685543340389,\"y\":20.5},{\"x\":1685543640471,\"y\":20.8},{\"x\":1685543940473,\"y\":20.5},{\"x\":1685544240621,\"y\":20.8},{\"x\":1685544540685,\"y\":20.5},{\"x\":1685544840799,\"y\":20.7},{\"x\":1685545140901,\"y\":20.5},{\"x\":1685545441001,\"y\":20.8},{\"x\":1685545741065,\"y\":20.6},{\"x\":1685546041186,\"y\":20.5},{\"x\":1685546341243,\"y\":20.7},{\"x\":1685546641292,\"y\":20.4},{\"x\":1685546941376,\"y\":20.8},{\"x\":1685547241438,\"y\":20.5},{\"x\":1685547541565,\"y\":20.8},{\"x\":1685547841634,\"y\":20.5},{\"x\":1685548141711,\"y\":20.8},{\"x\":1685548441854,\"y\":20.6},{\"x\":1685548741902,\"y\":20.5},{\"x\":1685549042002,\"y\":20.8},{\"x\":1685549342099,\"y\":20.5},{\"x\":1685549642156,\"y\":20.8},{\"x\":1685549942238,\"y\":20.7},{\"x\":1685550242287,\"y\":20.5},{\"x\":1685550542321,\"y\":20.8},{\"x\":1685550842435,\"y\":20.7},{\"x\":1685551142532,\"y\":20.5},{\"x\":1685551442679,\"y\":20.8},{\"x\":1685551742742,\"y\":20.8},{\"x\":1685552042824,\"y\":20.7},{\"x\":1685552342904,\"y\":20.6},{\"x\":1685552643024,\"y\":20.7},{\"x\":1685552943102,\"y\":20.7},{\"x\":1685553243197,\"y\":20.7},{\"x\":1685553543262,\"y\":20.7},{\"x\":1685553843353,\"y\":20.8},{\"x\":1685554143366,\"y\":20.8},{\"x\":1685554443531,\"y\":20.8},{\"x\":1685554743589,\"y\":20.8},{\"x\":1685555043690,\"y\":20.8},{\"x\":1685555343799,\"y\":20.8},{\"x\":1685555643872,\"y\":20.8},{\"x\":1685555943930,\"y\":20.8},{\"x\":1685556244056,\"y\":20.8},{\"x\":1685556544148,\"y\":20.8},{\"x\":1685556844209,\"y\":20.8},{\"x\":1685557144344,\"y\":20.7},{\"x\":1685557444363,\"y\":20.8},{\"x\":1685557744405,\"y\":20.7},{\"x\":1685558044539,\"y\":20.7},{\"x\":1685558344605,\"y\":20.7},{\"x\":1685558644713,\"y\":20.7},{\"x\":1685558944785,\"y\":20.7},{\"x\":1685559244901,\"y\":20.6},{\"x\":1685559544945,\"y\":20.6},{\"x\":1685559845042,\"y\":20.5},{\"x\":1685560145143,\"y\":20.5},{\"x\":1685560445241,\"y\":20.6},{\"x\":1685560745257,\"y\":20.5},{\"x\":1685561045328,\"y\":20.5},{\"x\":1685561345445,\"y\":20.5},{\"x\":1685561645525,\"y\":20.6},{\"x\":1685561945646,\"y\":20.8},{\"x\":1685562245696,\"y\":20.7},{\"x\":1685562545795,\"y\":20.6},{\"x\":1685562845871,\"y\":20.7},{\"x\":1685563145971,\"y\":20.8},{\"x\":1685563446094,\"y\":20.7},{\"x\":1685563746173,\"y\":20.5},{\"x\":1685564046283,\"y\":20.8},{\"x\":1685564346314,\"y\":20.8},{\"x\":1685564646386,\"y\":20.7},{\"x\":1685564946460,\"y\":20.6},{\"x\":1685565246561,\"y\":20.5},{\"x\":1685565546656,\"y\":20.7},{\"x\":1685565846745,\"y\":20.8},{\"x\":1685566146812,\"y\":20.6},{\"x\":1685566446933,\"y\":20.7},{\"x\":1685566746982,\"y\":20.7},{\"x\":1685567047084,\"y\":20.5},{\"x\":1685567347176,\"y\":20.8},{\"x\":1685567647243,\"y\":20.5},{\"x\":1685567947275,\"y\":20.8},{\"x\":1685568247414,\"y\":20.8},{\"x\":1685568547487,\"y\":20.8},{\"x\":1685568847582,\"y\":20.6},{\"x\":1685569147698,\"y\":20.6},{\"x\":1685569447750,\"y\":20.5},{\"x\":1685569747835,\"y\":20.8},{\"x\":1685570047948,\"y\":20.7},{\"x\":1685570348041,\"y\":20.5},{\"x\":1685570648135,\"y\":20.7},{\"x\":1685570948186,\"y\":20.7},{\"x\":1685571248285,\"y\":20.5},{\"x\":1685571548397,\"y\":20.7},{\"x\":1685571848461,\"y\":20.6},{\"x\":1685572148552,\"y\":20.7},{\"x\":1685572448646,\"y\":20.8},{\"x\":1685572748724,\"y\":20.7},{\"x\":1685573048812,\"y\":20.4},{\"x\":1685573348924,\"y\":20.8},{\"x\":1685573648998,\"y\":20.7},{\"x\":1685573949035,\"y\":20.5},{\"x\":1685574249162,\"y\":20.7},{\"x\":1685574564170,\"y\":20.5},{\"x\":1685574864211,\"y\":20.3},{\"x\":1685575164295,\"y\":20.3},{\"x\":1685575464392,\"y\":20.3},{\"x\":1685575764473,\"y\":20.2},{\"x\":1685576064520,\"y\":20},{\"x\":1685576364561,\"y\":19.9},{\"x\":1685576664649,\"y\":19.6},{\"x\":1685576964764,\"y\":19.5},{\"x\":1685577264783,\"y\":19.5},{\"x\":1685577564825,\"y\":19.5},{\"x\":1685577864898,\"y\":19.5},{\"x\":1685578165022,\"y\":19.5},{\"x\":1685578465041,\"y\":19.5},{\"x\":1685578765123,\"y\":19.5},{\"x\":1685579065190,\"y\":19.5},{\"x\":1685579365242,\"y\":19.5},{\"x\":1685579665270,\"y\":19.5},{\"x\":1685579965342,\"y\":19.5},{\"x\":1685580265419,\"y\":19.5},{\"x\":1685580565474,\"y\":19.5},{\"x\":1685580865528,\"y\":19.5},{\"x\":1685581165617,\"y\":19.5},{\"x\":1685581465707,\"y\":19.5},{\"x\":1685581765780,\"y\":19.5},{\"x\":1685582065871,\"y\":19.5},{\"x\":1685582365914,\"y\":19.5},{\"x\":1685582665994,\"y\":19.5},{\"x\":1685582966029,\"y\":19.5},{\"x\":1685583266054,\"y\":19.5},{\"x\":1685583566137,\"y\":19.5},{\"x\":1685583866167,\"y\":19.5},{\"x\":1685584166249,\"y\":19.5},{\"x\":1685584466332,\"y\":19.5},{\"x\":1685584766387,\"y\":19.5},{\"x\":1685585066444,\"y\":19.5},{\"x\":1685585366558,\"y\":19.5},{\"x\":1685585666591,\"y\":19.5},{\"x\":1685585966705,\"y\":19.5},{\"x\":1685586281700,\"y\":19.5},{\"x\":1685586581763,\"y\":19.5},{\"x\":1685586881831,\"y\":19.5},{\"x\":1685587181935,\"y\":19.5},{\"x\":1685587481999,\"y\":19.4},{\"x\":1685587782043,\"y\":19.4},{\"x\":1685588082116,\"y\":19.4},{\"x\":1685588382188,\"y\":19.4},{\"x\":1685588682255,\"y\":19.4},{\"x\":1685588982296,\"y\":19.4},{\"x\":1685589282341,\"y\":19.3},{\"x\":1685589582405,\"y\":19.4},{\"x\":1685589882472,\"y\":19.4},{\"x\":1685590182540,\"y\":19.3},{\"x\":1685590482618,\"y\":19.3},{\"x\":1685590782722,\"y\":19.3},{\"x\":1685591082771,\"y\":19.3},{\"x\":1685591382819,\"y\":19.2},{\"x\":1685591682931,\"y\":19.2},{\"x\":1685591982967,\"y\":19.2},{\"x\":1685592282986,\"y\":19.2},{\"x\":1685592583082,\"y\":19.2},{\"x\":1685592883165,\"y\":19.2},{\"x\":1685593183195,\"y\":19.2},{\"x\":1685593483277,\"y\":19.1},{\"x\":1685593783380,\"y\":19.2},{\"x\":1685594083450,\"y\":19.2},{\"x\":1685594383491,\"y\":19.2},{\"x\":1685594683592,\"y\":19.1},{\"x\":1685594983618,\"y\":19.1},{\"x\":1685595283711,\"y\":19.1},{\"x\":1685595583730,\"y\":19.1},{\"x\":1685595883817,\"y\":19},{\"x\":1685596183896,\"y\":19},{\"x\":1685596483953,\"y\":19},{\"x\":1685596784061,\"y\":19},{\"x\":1685597084124,\"y\":19},{\"x\":1685597384139,\"y\":19.1},{\"x\":1685597684213,\"y\":19},{\"x\":1685597984240,\"y\":19},{\"x\":1685598284312,\"y\":19},{\"x\":1685598584338,\"y\":19},{\"x\":1685598884381,\"y\":19},{\"x\":1685599184436,\"y\":19},{\"x\":1685599499407,\"y\":19},{\"x\":1685599799490,\"y\":18.9},{\"x\":1685600099528,\"y\":19},{\"x\":1685600399539,\"y\":19},{\"x\":1685600699637,\"y\":19},{\"x\":1685600999644,\"y\":19},{\"x\":1685601299689,\"y\":19},{\"x\":1685601599758,\"y\":19},{\"x\":1685601899826,\"y\":19},{\"x\":1685602199848,\"y\":18.9},{\"x\":1685602499869,\"y\":18.9},{\"x\":1685602799939,\"y\":19},{\"x\":1685603099979,\"y\":18.9},{\"x\":1685603414978,\"y\":18.9},{\"x\":1685603715053,\"y\":19},{\"x\":1685604030039,\"y\":19},{\"x\":1685604330117,\"y\":19},{\"x\":1685604630167,\"y\":19},{\"x\":1685604930199,\"y\":19},{\"x\":1685605230238,\"y\":18.9},{\"x\":1685605530260,\"y\":19},{\"x\":1685605830318,\"y\":19},{\"x\":1685606130319,\"y\":19},{\"x\":1685606430335,\"y\":19},{\"x\":1685606730393,\"y\":19},{\"x\":1685607030458,\"y\":19},{\"x\":1685607330485,\"y\":19.5},{\"x\":1685607630516,\"y\":20.3},{\"x\":1685607930568,\"y\":20.6},{\"x\":1685608230576,\"y\":20.4},{\"x\":1685608530643,\"y\":20.5},{\"x\":1685608830684,\"y\":20.5},{\"x\":1685609130723,\"y\":20.5},{\"x\":1685609430778,\"y\":20.5},{\"x\":1685609745753,\"y\":20.7},{\"x\":1685610045798,\"y\":20.4},{\"x\":1685610345834,\"y\":20.7},{\"x\":1685610645876,\"y\":20.4},{\"x\":1685610945921,\"y\":20.8},{\"x\":1685611245935,\"y\":20.5},{\"x\":1685611545963,\"y\":20.8},{\"x\":1685611845989,\"y\":20.6},{\"x\":1685612146117,\"y\":20.5},{\"x\":1685612446189,\"y\":20.8},{\"x\":1685612746312,\"y\":20.6},{\"x\":1685613046346,\"y\":20.5},{\"x\":1685613346425,\"y\":20.8},{\"x\":1685613646580,\"y\":20.6},{\"x\":1685613961556,\"y\":20.6},{\"x\":1685614261648,\"y\":20.7},{\"x\":1685614576669,\"y\":20.7},{\"x\":1685614876695,\"y\":20.5},{\"x\":1685615176733,\"y\":20.4},{\"x\":1685615476808,\"y\":20.7},{\"x\":1685615776863,\"y\":20.7},{\"x\":1685616076917,\"y\":20.5},{\"x\":1685616376987,\"y\":20.4},{\"x\":1685616677033,\"y\":20.3},{\"x\":1685616977113,\"y\":20.3},{\"x\":1685617277203,\"y\":20.3},{\"x\":1685617577236,\"y\":20.3},{\"x\":1685617877275,\"y\":20.2},{\"x\":1685618177324,\"y\":20.3},{\"x\":1685618477408,\"y\":20.2},{\"x\":1685618777447,\"y\":20.3},{\"x\":1685619077601,\"y\":20.2},{\"x\":1685619377646,\"y\":20.2},{\"x\":1685619677730,\"y\":20.1},{\"x\":1685619977761,\"y\":20.2},{\"x\":1685620017473,\"y\":20.1},{\"x\":1685620080667,\"y\":20.1}],",[1107,29080,29081],{},"{\"x\":1685015251039,\"y\":19.18},{\"x\":1685015551015,\"y\":19.18},{\"x\":1685015851010,\"y\":19.18},{\"x\":1685016151028,\"y\":19.09},{\"x\":1685016451042,\"y\":18.75},{\"x\":1685016751019,\"y\":18.66},{\"x\":1685017051028,\"y\":18.75},{\"x\":1685017351047,\"y\":18.89},{\"x\":1685017651031,\"y\":18.8},{\"x\":1685017951098,\"y\":18.83},{\"x\":1685018251018,\"y\":18.3},{\"x\":1685018551020,\"y\":18.3},{\"x\":1685018851032,\"y\":18.36},{\"x\":1685019151038,\"y\":18.36},{\"x\":1685019451035,\"y\":19.14},{\"x\":1685019751031,\"y\":19.04},{\"x\":1685020051038,\"y\":19.64},{\"x\":1685020351040,\"y\":19.42},{\"x\":1685020651063,\"y\":19.88},{\"x\":1685020951079,\"y\":19.73},{\"x\":1685021251036,\"y\":19.73},{\"x\":1685021551137,\"y\":19.54},{\"x\":1685021851047,\"y\":19.21},{\"x\":1685022151057,\"y\":19.13},{\"x\":1685022451042,\"y\":19.21},{\"x\":1685022751080,\"y\":19.1},{\"x\":1685023051043,\"y\":18.46},{\"x\":1685023351058,\"y\":18.46},{\"x\":1685023651118,\"y\":18.47},{\"x\":1685023951188,\"y\":18.48},{\"x\":1685024251055,\"y\":18.48},{\"x\":1685024551051,\"y\":18.44},{\"x\":1685024851052,\"y\":18.44},{\"x\":1685025151135,\"y\":18.73},{\"x\":1685025451076,\"y\":18.73},{\"x\":1685025751061,\"y\":18.73},{\"x\":1685026051071,\"y\":19.02},{\"x\":1685026351158,\"y\":19.05},{\"x\":1685026651115,\"y\":19.03},{\"x\":1685026951071,\"y\":19.03},{\"x\":1685027251066,\"y\":19.03},{\"x\":1685027551148,\"y\":18.62},{\"x\":1685027851058,\"y\":18.62},{\"x\":1685028151059,\"y\":18.62},{\"x\":1685028451112,\"y\":19},{\"x\":1685028751146,\"y\":18.91},{\"x\":1685029051091,\"y\":18.1},{\"x\":1685029351104,\"y\":18.12},{\"x\":1685029651134,\"y\":18.51},{\"x\":1685029951350,\"y\":18.12},{\"x\":1685030251085,\"y\":18.51},{\"x\":1685030551087,\"y\":18.42},{\"x\":1685030851113,\"y\":18.46},{\"x\":1685031151160,\"y\":18.37},{\"x\":1685031451109,\"y\":18.33},{\"x\":1685031751080,\"y\":18.21},{\"x\":1685032051082,\"y\":18.3},{\"x\":1685032351518,\"y\":18.3},{\"x\":1685032651206,\"y\":17.89},{\"x\":1685032951081,\"y\":17.8},{\"x\":1685033251086,\"y\":17.8},{\"x\":1685033551096,\"y\":17.8},{\"x\":1685033851075,\"y\":17.8},{\"x\":1685034151086,\"y\":17.78},{\"x\":1685034451139,\"y\":17.63},{\"x\":1685034751066,\"y\":17.63},{\"x\":1685035051075,\"y\":17.63},{\"x\":1685035351084,\"y\":17.54},{\"x\":1685035651091,\"y\":17.54},{\"x\":1685035951102,\"y\":17.55},{\"x\":1685036251109,\"y\":17.28},{\"x\":1685036551080,\"y\":17.54},{\"x\":1685036851085,\"y\":17.25},{\"x\":1685037151113,\"y\":17.2},{\"x\":1685037451085,\"y\":16.99},{\"x\":1685037751124,\"y\":16.99},{\"x\":1685038051100,\"y\":16.99},{\"x\":1685038351547,\"y\":17.1},{\"x\":1685038651116,\"y\":17.02},{\"x\":1685038951187,\"y\":16.94},{\"x\":1685039251190,\"y\":16.88},{\"x\":1685039551190,\"y\":16.85},{\"x\":1685039851181,\"y\":16.85},{\"x\":1685040151138,\"y\":16.82},{\"x\":1685040451132,\"y\":16.79},{\"x\":1685040751183,\"y\":16.79},{\"x\":1685041051160,\"y\":16.79},{\"x\":1685041351171,\"y\":16.7},{\"x\":1685041651173,\"y\":16.44},{\"x\":1685041951215,\"y\":16.44},{\"x\":1685042251220,\"y\":16.48},{\"x\":1685042551262,\"y\":16.52},{\"x\":1685042851204,\"y\":16.48},{\"x\":1685043151252,\"y\":16.52},{\"x\":1685043451209,\"y\":16.4},{\"x\":1685043751246,\"y\":16.26},{\"x\":1685044051210,\"y\":16.26},{\"x\":1685044351522,\"y\":16.26},{\"x\":1685044651215,\"y\":16.17},{\"x\":1685044951218,\"y\":16.17},{\"x\":1685045251281,\"y\":15.98},{\"x\":1685045551345,\"y\":15.98},{\"x\":1685045851232,\"y\":16.01},{\"x\":1685046151240,\"y\":15.98},{\"x\":1685046451276,\"y\":15.75},{\"x\":1685046751262,\"y\":15.75},{\"x\":1685047051266,\"y\":15.75},{\"x\":1685047351285,\"y\":15.68},{\"x\":1685047651267,\"y\":15.68},{\"x\":1685047951728,\"y\":15.49},{\"x\":1685048251321,\"y\":15.5},{\"x\":1685048551265,\"y\":15.49},{\"x\":1685048851352,\"y\":15.35},{\"x\":1685049151302,\"y\":15.35},{\"x\":1685049451264,\"y\":14.98},{\"x\":1685049751268,\"y\":14.98},{\"x\":1685050051322,\"y\":14.97},{\"x\":1685050351309,\"y\":14.97},{\"x\":1685050651266,\"y\":14.75},{\"x\":1685050951264,\"y\":14.75},{\"x\":1685051251276,\"y\":14.82},{\"x\":1685051551291,\"y\":14.73},{\"x\":1685051851384,\"y\":14.57},{\"x\":1685052151274,\"y\":14.41},{\"x\":1685052451281,\"y\":14.41},{\"x\":1685052751280,\"y\":14.35},{\"x\":1685053051279,\"y\":14.49},{\"x\":1685053351326,\"y\":14.29},{\"x\":1685053651310,\"y\":14.3},{\"x\":1685053951323,\"y\":14.29},{\"x\":1685054251377,\"y\":13.95},{\"x\":1685054551303,\"y\":13.95},{\"x\":1685054851307,\"y\":13.95},{\"x\":1685055151655,\"y\":13.71},{\"x\":1685055451302,\"y\":13.71},{\"x\":1685055751335,\"y\":13.58},{\"x\":1685056051330,\"y\":13.55},{\"x\":1685056351435,\"y\":13.58},{\"x\":1685056651370,\"y\":13.33},{\"x\":1685056951348,\"y\":13.58},{\"x\":1685057251335,\"y\":13.23},{\"x\":1685057551642,\"y\":13.23},{\"x\":1685057851342,\"y\":13.23},{\"x\":1685058151360,\"y\":12.98},{\"x\":1685058451395,\"y\":13},{\"x\":1685058751511,\"y\":13.01},{\"x\":1685059051391,\"y\":13.01},{\"x\":1685059351339,\"y\":12.9},{\"x\":1685059651359,\"y\":12.9},{\"x\":1685059951438,\"y\":12.48},{\"x\":1685060251356,\"y\":12.48},{\"x\":1685060551372,\"y\":12.48},{\"x\":1685060851371,\"y\":12.32},{\"x\":1685061151374,\"y\":12.32},{\"x\":1685061451412,\"y\":11.72},{\"x\":1685061751394,\"y\":11.63},{\"x\":1685062051381,\"y\":11.72},{\"x\":1685062352047,\"y\":11.55},{\"x\":1685062651444,\"y\":11.36},{\"x\":1685062951373,\"y\":11.46},{\"x\":1685063251410,\"y\":11.36},{\"x\":1685063551479,\"y\":11.37},{\"x\":1685063851380,\"y\":11.36},{\"x\":1685064151391,\"y\":11.44},{\"x\":1685064451471,\"y\":11.34},{\"x\":1685064751589,\"y\":11.32},{\"x\":1685065051396,\"y\":11.26},{\"x\":1685065351399,\"y\":11.22},{\"x\":1685065651409,\"y\":11.39},{\"x\":1685065952272,\"y\":11.33},{\"x\":1685066251396,\"y\":11.21},{\"x\":1685066551403,\"y\":11.21},{\"x\":1685066851417,\"y\":11.34},{\"x\":1685067151442,\"y\":11.28},{\"x\":1685067451399,\"y\":11.27},{\"x\":1685067751460,\"y\":11.06},{\"x\":1685068051420,\"y\":11.27},{\"x\":1685068351485,\"y\":11.02},{\"x\":1685068651412,\"y\":11.02},{\"x\":1685068951402,\"y\":10.91},{\"x\":1685069251450,\"y\":11},{\"x\":1685069551482,\"y\":11},{\"x\":1685069851414,\"y\":10.88},{\"x\":1685070151414,\"y\":10.66},{\"x\":1685070451408,\"y\":10.66},{\"x\":1685070752331,\"y\":10.76},{\"x\":1685071051424,\"y\":10.76},{\"x\":1685071351428,\"y\":10.76},{\"x\":1685071651498,\"y\":10.55},{\"x\":1685071951451,\"y\":10.55},{\"x\":1685072251483,\"y\":10.55},{\"x\":1685072551428,\"y\":10.55},{\"x\":1685072851477,\"y\":10.34},{\"x\":1685073151490,\"y\":10.34},{\"x\":1685073451401,\"y\":10.34},{\"x\":1685073751433,\"y\":10.08},{\"x\":1685074051433,\"y\":10.08},{\"x\":1685074351659,\"y\":9.98},{\"x\":1685074651481,\"y\":9.98},{\"x\":1685074951442,\"y\":9.98},{\"x\":1685075251486,\"y\":9.76},{\"x\":1685075553145,\"y\":9.76},{\"x\":1685075851454,\"y\":9.76},{\"x\":1685076151559,\"y\":9.76},{\"x\":1685076451455,\"y\":9.76},{\"x\":1685076751637,\"y\":9.76},{\"x\":1685077051562,\"y\":10.23},{\"x\":1685077351466,\"y\":10.23},{\"x\":1685077651495,\"y\":10.23},{\"x\":1685077951596,\"y\":10.15},{\"x\":1685078251465,\"y\":10.36},{\"x\":1685078551490,\"y\":10.36},{\"x\":1685078851486,\"y\":10.82},{\"x\":1685079152413,\"y\":10.82},{\"x\":1685079451481,\"y\":11.06},{\"x\":1685079751484,\"y\":11.06},{\"x\":1685080051515,\"y\":11.26},{\"x\":1685080353153,\"y\":11.08},{\"x\":1685080651466,\"y\":11.37},{\"x\":1685080951559,\"y\":11.85},{\"x\":1685081251496,\"y\":12.07},{\"x\":1685081552662,\"y\":11.54},{\"x\":1685081851505,\"y\":11.54},{\"x\":1685082151498,\"y\":12.9},{\"x\":1685082451574,\"y\":11.98},{\"x\":1685082752768,\"y\":12.05},{\"x\":1685083051516,\"y\":12.05},{\"x\":1685083351518,\"y\":12.54},{\"x\":1685083651522,\"y\":12.54},{\"x\":1685083951825,\"y\":13.01},{\"x\":1685084251526,\"y\":13.01},{\"x\":1685084551531,\"y\":13.84},{\"x\":1685084851537,\"y\":14.15},{\"x\":1685085154315,\"y\":14.2},{\"x\":1685085451540,\"y\":14.2},{\"x\":1685085751599,\"y\":14.74},{\"x\":1685086051532,\"y\":14.65},{\"x\":1685086352440,\"y\":14.65},{\"x\":1685086651538,\"y\":15.31},{\"x\":1685086951544,\"y\":15.31},{\"x\":1685087251583,\"y\":15.43},{\"x\":1685087552469,\"y\":15.61},{\"x\":1685087851550,\"y\":15.43},{\"x\":1685088151549,\"y\":15.75},{\"x\":1685088451547,\"y\":15.75},{\"x\":1685088752994,\"y\":16.44},{\"x\":1685089051545,\"y\":16.44},{\"x\":1685089351589,\"y\":16.84},{\"x\":1685089651598,\"y\":16.44},{\"x\":1685089952825,\"y\":16.84},{\"x\":1685090251561,\"y\":17.35},{\"x\":1685090551636,\"y\":17.66},{\"x\":1685090851564,\"y\":17.56},{\"x\":1685091152016,\"y\":17.66},{\"x\":1685091451594,\"y\":17.93},{\"x\":1685091751567,\"y\":18.04},{\"x\":1685092051581,\"y\":17.93},{\"x\":1685092352469,\"y\":18.08},{\"x\":1685092651613,\"y\":18.17},{\"x\":1685092951598,\"y\":18.25},{\"x\":1685093251572,\"y\":18.23},{\"x\":1685093551684,\"y\":18.43},{\"x\":1685093851596,\"y\":18.5},{\"x\":1685094151578,\"y\":18.5},{\"x\":1685094451581,\"y\":18.85},{\"x\":1685094753849,\"y\":18.85},{\"x\":1685095051634,\"y\":19.17},{\"x\":1685095351592,\"y\":19.09},{\"x\":1685095651582,\"y\":19.37},{\"x\":1685095952262,\"y\":19.37},{\"x\":1685096251665,\"y\":19.46},{\"x\":1685096551653,\"y\":19.46},{\"x\":1685096851597,\"y\":19.46},{\"x\":1685097151894,\"y\":19.32},{\"x\":1685097451605,\"y\":19.59},{\"x\":1685097751603,\"y\":19.85},{\"x\":1685098051603,\"y\":19.75},{\"x\":1685098351825,\"y\":19.65},{\"x\":1685098651611,\"y\":19.82},{\"x\":1685098951612,\"y\":19.65},{\"x\":1685099251620,\"y\":19.57},{\"x\":1685099554557,\"y\":19.84},{\"x\":1685099851611,\"y\":19.98},{\"x\":1685100151610,\"y\":19.98},{\"x\":1685100451674,\"y\":19.9},{\"x\":1685100751631,\"y\":19.9},{\"x\":1685101051671,\"y\":20.45},{\"x\":1685101351650,\"y\":20.45},{\"x\":1685101651625,\"y\":20.45},{\"x\":1685101952592,\"y\":19.74},{\"x\":1685102251628,\"y\":19.74},{\"x\":1685102551620,\"y\":19.74},{\"x\":1685102851630,\"y\":19.84},{\"x\":1685103152663,\"y\":20.53},{\"x\":1685103451625,\"y\":20.53},{\"x\":1685103751638,\"y\":20.37},{\"x\":1685104051653,\"y\":20.37},{\"x\":1685104351690,\"y\":20.42},{\"x\":1685104651625,\"y\":20.92},{\"x\":1685104951669,\"y\":21.05},{\"x\":1685105251635,\"y\":20.92},{\"x\":1685106270209,\"y\":21.51},{\"x\":1685106570210,\"y\":21.28},{\"x\":1685106870262,\"y\":21.31},{\"x\":1685107170259,\"y\":20.87},{\"x\":1685107470202,\"y\":20.87},{\"x\":1685107770205,\"y\":20.87},{\"x\":1685108070201,\"y\":20.24},{\"x\":1685108370268,\"y\":20.24},{\"x\":1685108670211,\"y\":20.81},{\"x\":1685108970215,\"y\":20.61},{\"x\":1685109270264,\"y\":21.02},{\"x\":1685109570225,\"y\":21.02},{\"x\":1685109870210,\"y\":21.35},{\"x\":1685110170276,\"y\":21.42},{\"x\":1685110470215,\"y\":21.08},{\"x\":1685110770218,\"y\":21.08},{\"x\":1685111070267,\"y\":21.3},{\"x\":1685111370237,\"y\":21.32},{\"x\":1685111670242,\"y\":21.21},{\"x\":1685111970278,\"y\":21.56},{\"x\":1685112270243,\"y\":21.43},{\"x\":1685112570286,\"y\":20.62},{\"x\":1685112870312,\"y\":20.61},{\"x\":1685113170279,\"y\":20.61},{\"x\":1685113470259,\"y\":20.52},{\"x\":1685113770274,\"y\":21.03},{\"x\":1685114070330,\"y\":20.96},{\"x\":1685114370346,\"y\":20.62},{\"x\":1685114670290,\"y\":20.62},{\"x\":1685114970303,\"y\":20.62},{\"x\":1685115270293,\"y\":20.57},{\"x\":1685115570345,\"y\":20.81},{\"x\":1685115870301,\"y\":20.81},{\"x\":1685116170341,\"y\":20.85},{\"x\":1685116470425,\"y\":20.35},{\"x\":1685116770319,\"y\":20.73},{\"x\":1685117070306,\"y\":20.35},{\"x\":1685117370344,\"y\":20.75},{\"x\":1685117670330,\"y\":20.75},{\"x\":1685117970336,\"y\":20.75},{\"x\":1685118270330,\"y\":20.45},{\"x\":1685118570325,\"y\":20.45},{\"x\":1685118870362,\"y\":19.95},{\"x\":1685119170359,\"y\":19.95},{\"x\":1685119470369,\"y\":19.95},{\"x\":1685119770365,\"y\":19.92},{\"x\":1685120070363,\"y\":20.06},{\"x\":1685120370385,\"y\":20.17},{\"x\":1685120670359,\"y\":20.17},{\"x\":1685120970383,\"y\":20.08},{\"x\":1685121270373,\"y\":19.95},{\"x\":1685121570376,\"y\":19.85},{\"x\":1685121870394,\"y\":19.88},{\"x\":1685122170385,\"y\":19.88},{\"x\":1685122470467,\"y\":19},{\"x\":1685122770415,\"y\":19},{\"x\":1685123070412,\"y\":19},{\"x\":1685123370409,\"y\":18.86},{\"x\":1685123670402,\"y\":18.86},{\"x\":1685123970418,\"y\":18.86},{\"x\":1685124270399,\"y\":18.86},{\"x\":1685124570415,\"y\":18.79},{\"x\":1685124870446,\"y\":18.79},{\"x\":1685125170457,\"y\":18.85},{\"x\":1685125470494,\"y\":18.85},{\"x\":1685125770424,\"y\":18.85},{\"x\":1685126070436,\"y\":18.71},{\"x\":1685126370464,\"y\":18.71},{\"x\":1685126670446,\"y\":18.67},{\"x\":1685126970458,\"y\":18.67},{\"x\":1685127270730,\"y\":17.76},{\"x\":1685127570540,\"y\":17.84},{\"x\":1685127870479,\"y\":17.42},{\"x\":1685128170488,\"y\":17.42},{\"x\":1685128470446,\"y\":17.32},{\"x\":1685128770711,\"y\":17.38},{\"x\":1685129070493,\"y\":17.55},{\"x\":1685129370460,\"y\":17.46},{\"x\":1685129670456,\"y\":17.51},{\"x\":1685129970444,\"y\":16.98},{\"x\":1685130270452,\"y\":16.94},{\"x\":1685130570463,\"y\":16.94},{\"x\":1685130870454,\"y\":16.63},{\"x\":1685131170558,\"y\":16.63},{\"x\":1685131470459,\"y\":16.63},{\"x\":1685131770480,\"y\":15.83},{\"x\":1685132070539,\"y\":15.61},{\"x\":1685132370654,\"y\":15.61},{\"x\":1685132670510,\"y\":15.17},{\"x\":1685132970470,\"y\":15.17},{\"x\":1685133270483,\"y\":15.17},{\"x\":1685133570534,\"y\":15.33},{\"x\":1685133870537,\"y\":15.17},{\"x\":1685134170481,\"y\":15.33},{\"x\":1685134470522,\"y\":15.17},{\"x\":1685134770619,\"y\":15.17},{\"x\":1685135070468,\"y\":15.17},{\"x\":1685135370539,\"y\":14.12},{\"x\":1685135670470,\"y\":14.12},{\"x\":1685135970588,\"y\":14.12},{\"x\":1685136270506,\"y\":13.94},{\"x\":1685136570482,\"y\":13.94},{\"x\":1685136870483,\"y\":13.88},{\"x\":1685137170591,\"y\":13.53},{\"x\":1685137470482,\"y\":13.29},{\"x\":1685137770513,\"y\":13.29},{\"x\":1685138070565,\"y\":13.24},{\"x\":1685138370612,\"y\":13.3},{\"x\":1685138670513,\"y\":12.65},{\"x\":1685138970488,\"y\":13.03},{\"x\":1685139270567,\"y\":12.44},{\"x\":1685139570520,\"y\":12.44},{\"x\":1685139870495,\"y\":12.47},{\"x\":1685140170514,\"y\":12.41},{\"x\":1685140470469,\"y\":12.41},{\"x\":1685140770566,\"y\":12.32},{\"x\":1685141070532,\"y\":11.91},{\"x\":1685141370505,\"y\":11.91},{\"x\":1685141670535,\"y\":11.91},{\"x\":1685141970572,\"y\":11.87},{\"x\":1685142270528,\"y\":11.87},{\"x\":1685142570539,\"y\":11.49},{\"x\":1685142870540,\"y\":11.49},{\"x\":1685143170721,\"y\":11.69},{\"x\":1685143470532,\"y\":11.69},{\"x\":1685143770537,\"y\":11.69},{\"x\":1685144070587,\"y\":11.38},{\"x\":1685144370585,\"y\":11.29},{\"x\":1685144670544,\"y\":11.38},{\"x\":1685144970592,\"y\":11.66},{\"x\":1685145270578,\"y\":11.66},{\"x\":1685145570616,\"y\":11.66},{\"x\":1685145870578,\"y\":11.54},{\"x\":1685146170575,\"y\":11.3},{\"x\":1685146470586,\"y\":11.26},{\"x\":1685146770618,\"y\":10.97},{\"x\":1685147070575,\"y\":10.97},{\"x\":1685147370627,\"y\":11.06},{\"x\":1685147670593,\"y\":10.78},{\"x\":1685147970600,\"y\":11.06},{\"x\":1685148270605,\"y\":10.65},{\"x\":1685148570644,\"y\":10.63},{\"x\":1685148870584,\"y\":10.63},{\"x\":1685149170590,\"y\":10.63},{\"x\":1685149470587,\"y\":10.44},{\"x\":1685149770583,\"y\":10.44},{\"x\":1685150070580,\"y\":10.1},{\"x\":1685150370584,\"y\":10.1},{\"x\":1685150670582,\"y\":10.03},{\"x\":1685150970594,\"y\":10.03},{\"x\":1685151270632,\"y\":10.08},{\"x\":1685151570591,\"y\":10.08},{\"x\":1685151870605,\"y\":9.61},{\"x\":1685152170611,\"y\":9.61},{\"x\":1685152470611,\"y\":9.42},{\"x\":1685152770613,\"y\":9.42},{\"x\":1685153070628,\"y\":9.48},{\"x\":1685153370683,\"y\":9.39},{\"x\":1685153670664,\"y\":9.33},{\"x\":1685153970636,\"y\":9.33},{\"x\":1685154270611,\"y\":9.33},{\"x\":1685154570650,\"y\":9.14},{\"x\":1685154870623,\"y\":9.14},{\"x\":1685155170939,\"y\":9.05},{\"x\":1685155470674,\"y\":8.95},{\"x\":1685155770628,\"y\":8.95},{\"x\":1685156070642,\"y\":8.86},{\"x\":1685156370678,\"y\":8.95},{\"x\":1685156670691,\"y\":8.95},{\"x\":1685156970647,\"y\":8.95},{\"x\":1685157270649,\"y\":8.48},{\"x\":1685157570728,\"y\":8.51},{\"x\":1685157870653,\"y\":8.67},{\"x\":1685158170653,\"y\":8.67},{\"x\":1685158470652,\"y\":8.47},{\"x\":1685158770755,\"y\":8.29},{\"x\":1685159070671,\"y\":8.29},{\"x\":1685159370691,\"y\":8.19},{\"x\":1685159670661,\"y\":8.19},{\"x\":1685159971379,\"y\":8.08},{\"x\":1685160270711,\"y\":8.04},{\"x\":1685160570739,\"y\":8.22},{\"x\":1685160870655,\"y\":8.22},{\"x\":1685161170787,\"y\":8.04},{\"x\":1685161470703,\"y\":8.24},{\"x\":1685161770668,\"y\":8.04},{\"x\":1685162070717,\"y\":8.02},{\"x\":1685162371170,\"y\":7.93},{\"x\":1685162670708,\"y\":8.02},{\"x\":1685162970719,\"y\":8.55},{\"x\":1685163270676,\"y\":8.55},{\"x\":1685163571126,\"y\":8.55},{\"x\":1685163870678,\"y\":8.77},{\"x\":1685164170681,\"y\":8.69},{\"x\":1685164470773,\"y\":9.46},{\"x\":1685164771057,\"y\":9.68},{\"x\":1685165070675,\"y\":9.97},{\"x\":1685165370677,\"y\":9.83},{\"x\":1685165670713,\"y\":10.29},{\"x\":1685165970916,\"y\":10.29},{\"x\":1685166270681,\"y\":10.47},{\"x\":1685166570684,\"y\":10.47},{\"x\":1685166870680,\"y\":11},{\"x\":1685167171095,\"y\":10.85},{\"x\":1685167470678,\"y\":11.53},{\"x\":1685167770689,\"y\":12.14},{\"x\":1685168070683,\"y\":12.14},{\"x\":1685168370787,\"y\":11.68},{\"x\":1685168670739,\"y\":11.68},{\"x\":1685168970743,\"y\":11.98},{\"x\":1685169270696,\"y\":11.98},{\"x\":1685169570699,\"y\":11.98},{\"x\":1685169870784,\"y\":13.66},{\"x\":1685170170701,\"y\":13.66},{\"x\":1685170470695,\"y\":13.66},{\"x\":1685170770757,\"y\":13.66},{\"x\":1685171070684,\"y\":13.98},{\"x\":1685171370697,\"y\":13.98},{\"x\":1685171670698,\"y\":14.38},{\"x\":1685171971138,\"y\":14.38},{\"x\":1685172270759,\"y\":13.85},{\"x\":1685172570699,\"y\":13.85},{\"x\":1685172870711,\"y\":13.85},{\"x\":1685173170778,\"y\":15.4},{\"x\":1685173470740,\"y\":15.5},{\"x\":1685173770702,\"y\":15.18},{\"x\":1685174070719,\"y\":15.49},{\"x\":1685174371094,\"y\":15.8},{\"x\":1685174670695,\"y\":15.49},{\"x\":1685174970800,\"y\":16.03},{\"x\":1685175270722,\"y\":15.86},{\"x\":1685175570763,\"y\":15.86},{\"x\":1685175870697,\"y\":16.35},{\"x\":1685176170707,\"y\":16.48},{\"x\":1685176470754,\"y\":16.94},{\"x\":1685176770748,\"y\":16.94},{\"x\":1685177070739,\"y\":17.05},{\"x\":1685177370721,\"y\":17.05},{\"x\":1685177670735,\"y\":17.05},{\"x\":1685177971115,\"y\":17.66},{\"x\":1685178270740,\"y\":17.66},{\"x\":1685178570747,\"y\":17.64},{\"x\":1685178870774,\"y\":18.01},{\"x\":1685179171072,\"y\":17.92},{\"x\":1685179470822,\"y\":18.19},{\"x\":1685179770758,\"y\":18.1},{\"x\":1685180070762,\"y\":18.19},{\"x\":1685180370805,\"y\":18.1},{\"x\":1685180670780,\"y\":18.51},{\"x\":1685180970809,\"y\":18.65},{\"x\":1685181270793,\"y\":18.72},{\"x\":1685181570900,\"y\":19.19},{\"x\":1685181870801,\"y\":19.1},{\"x\":1685182170811,\"y\":19.19},{\"x\":1685182470860,\"y\":19.44},{\"x\":1685182771048,\"y\":19.67},{\"x\":1685183070826,\"y\":19.44},{\"x\":1685183370877,\"y\":19.67},{\"x\":1685183670882,\"y\":19.85},{\"x\":1685183970939,\"y\":19.85},{\"x\":1685184270841,\"y\":19.85},{\"x\":1685184570853,\"y\":19.96},{\"x\":1685184870900,\"y\":20.18},{\"x\":1685185170967,\"y\":20.47},{\"x\":1685185470866,\"y\":20.18},{\"x\":1685185770871,\"y\":20.65},{\"x\":1685186070870,\"y\":20.65},{\"x\":1685186370892,\"y\":20.6},{\"x\":1685186670893,\"y\":20.57},{\"x\":1685186970904,\"y\":20.71},{\"x\":1685187270868,\"y\":20.71},{\"x\":1685187572989,\"y\":20.99},{\"x\":1685187870872,\"y\":20.99},{\"x\":1685188170877,\"y\":20.99},{\"x\":1685188470913,\"y\":21.13},{\"x\":1685188771470,\"y\":21.2},{\"x\":1685189070899,\"y\":21.12},{\"x\":1685189370894,\"y\":21.2},{\"x\":1685189670921,\"y\":21.41},{\"x\":1685189971610,\"y\":21.41},{\"x\":1685190270898,\"y\":21.41},{\"x\":1685190570940,\"y\":21.32},{\"x\":1685190870918,\"y\":21.32},{\"x\":1685191171751,\"y\":21.78},{\"x\":1685191470923,\"y\":21.68},{\"x\":1685191770922,\"y\":21.68},{\"x\":1685192070981,\"y\":22.17},{\"x\":1685192371026,\"y\":22.17},{\"x\":1685192670934,\"y\":22.17},{\"x\":1685192970954,\"y\":22.46},{\"x\":1685193270953,\"y\":22.46},{\"x\":1685193571076,\"y\":21.99},{\"x\":1685193870942,\"y\":21.91},{\"x\":1685194171071,\"y\":21.99},{\"x\":1685194470972,\"y\":21.91},{\"x\":1685194770981,\"y\":21.91},{\"x\":1685195070960,\"y\":23.06},{\"x\":1685195370975,\"y\":22.73},{\"x\":1685195670969,\"y\":23.06},{\"x\":1685195971032,\"y\":22.82},{\"x\":1685196270973,\"y\":22.82},{\"x\":1685196570982,\"y\":22.82},{\"x\":1685196870986,\"y\":21.82},{\"x\":1685197171084,\"y\":21.93},{\"x\":1685197470984,\"y\":22.31},{\"x\":1685197770988,\"y\":22.31},{\"x\":1685198070990,\"y\":22.31},{\"x\":1685198371020,\"y\":22.31},{\"x\":1685198671053,\"y\":22.37},{\"x\":1685198970993,\"y\":22.23},{\"x\":1685199271011,\"y\":22.37},{\"x\":1685199571046,\"y\":22.48},{\"x\":1685199871115,\"y\":21.98},{\"x\":1685200171006,\"y\":21.98},{\"x\":1685200471008,\"y\":21.98},{\"x\":1685200771192,\"y\":22.9},{\"x\":1685201071014,\"y\":22.9},{\"x\":1685201371067,\"y\":22.11},{\"x\":1685201671018,\"y\":22.11},{\"x\":1685201971372,\"y\":22.11},{\"x\":1685202271010,\"y\":21.91},{\"x\":1685202571020,\"y\":21.91},{\"x\":1685202871094,\"y\":22.37},{\"x\":1685203171957,\"y\":22.04},{\"x\":1685203471024,\"y\":21.92},{\"x\":1685203771087,\"y\":22.72},{\"x\":1685204071079,\"y\":22.72},{\"x\":1685204372043,\"y\":22.63},{\"x\":1685204671023,\"y\":22.63},{\"x\":1685204971067,\"y\":22.65},{\"x\":1685205271028,\"y\":22.65},{\"x\":1685205571490,\"y\":22.29},{\"x\":1685205871024,\"y\":21.93},{\"x\":1685206171037,\"y\":21.93},{\"x\":1685206471023,\"y\":21.53},{\"x\":1685206771057,\"y\":21.53},{\"x\":1685207071049,\"y\":20.84},{\"x\":1685207371039,\"y\":20.84},{\"x\":1685207671107,\"y\":20.92},{\"x\":1685207971068,\"y\":20.84},{\"x\":1685208271098,\"y\":22.62},{\"x\":1685208571110,\"y\":22.62},{\"x\":1685208871086,\"y\":22.62},{\"x\":1685209171774,\"y\":20.91},{\"x\":1685209471092,\"y\":20.82},{\"x\":1685209771092,\"y\":19.71},{\"x\":1685210071129,\"y\":19.73},{\"x\":1685210371211,\"y\":19.73},{\"x\":1685210671121,\"y\":19.24},{\"x\":1685210971142,\"y\":21.59},{\"x\":1685211271109,\"y\":21.59},{\"x\":1685211573673,\"y\":21.59},{\"x\":1685211871103,\"y\":21.59},{\"x\":1685212171150,\"y\":21.56},{\"x\":1685212471136,\"y\":21.47},{\"x\":1685212771652,\"y\":20.46},{\"x\":1685213071195,\"y\":20.46},{\"x\":1685213371188,\"y\":19.23},{\"x\":1685213671169,\"y\":19.15},{\"x\":1685213971280,\"y\":19.23},{\"x\":1685214271228,\"y\":19.41},{\"x\":1685214571150,\"y\":19.41},{\"x\":1685214871151,\"y\":19.24},{\"x\":1685215171157,\"y\":19.21},{\"x\":1685215471219,\"y\":19.21},{\"x\":1685215771223,\"y\":18.87},{\"x\":1685216071239,\"y\":18.72},{\"x\":1685216371187,\"y\":18.87},{\"x\":1685216671220,\"y\":16.48},{\"x\":1685216971167,\"y\":16.48},{\"x\":1685217271190,\"y\":16.48},{\"x\":1685217571385,\"y\":16.48},{\"x\":1685217871202,\"y\":16.48},{\"x\":1685218171176,\"y\":16.48},{\"x\":1685218471193,\"y\":17.13},{\"x\":1685218772083,\"y\":17.04},{\"x\":1685219071242,\"y\":16.94},{\"x\":1685219371182,\"y\":16.94},{\"x\":1685219671182,\"y\":16.94},{\"x\":1685219971327,\"y\":17.29},{\"x\":1685220271195,\"y\":17.28},{\"x\":1685220571204,\"y\":17.28},{\"x\":1685220871265,\"y\":16.43},{\"x\":1685221171213,\"y\":16.43},{\"x\":1685221471206,\"y\":16.43},{\"x\":1685221771244,\"y\":16.32},{\"x\":1685222071214,\"y\":16.32},{\"x\":1685222371240,\"y\":16.22},{\"x\":1685222671231,\"y\":15.63},{\"x\":1685222971227,\"y\":15.63},{\"x\":1685223271215,\"y\":15.63},{\"x\":1685223571642,\"y\":15.63},{\"x\":1685223871216,\"y\":14.69},{\"x\":1685224171238,\"y\":14.69},{\"x\":1685224471255,\"y\":15.05},{\"x\":1685224771718,\"y\":15.05},{\"x\":1685225071254,\"y\":15},{\"x\":1685225371225,\"y\":14.91},{\"x\":1685225671230,\"y\":14.91},{\"x\":1685225971278,\"y\":15.03},{\"x\":1685226271229,\"y\":14.76},{\"x\":1685226571268,\"y\":14.62},{\"x\":1685226871228,\"y\":14.62},{\"x\":1685227171310,\"y\":14.58},{\"x\":1685227471236,\"y\":14.47},{\"x\":1685227771233,\"y\":14.47},{\"x\":1685228071264,\"y\":14.56},{\"x\":1685228371438,\"y\":14.56},{\"x\":1685228671307,\"y\":14.37},{\"x\":1685228971287,\"y\":14.26},{\"x\":1685229271247,\"y\":14.17},{\"x\":1685229571283,\"y\":14.26},{\"x\":1685229871252,\"y\":14.08},{\"x\":1685230171240,\"y\":14.08},{\"x\":1685230471249,\"y\":13.81},{\"x\":1685230771547,\"y\":13.9},{\"x\":1685231071282,\"y\":13.88},{\"x\":1685231371247,\"y\":13.88},{\"x\":1685231671245,\"y\":13.57},{\"x\":1685231971299,\"y\":13.8},{\"x\":1685232271267,\"y\":13.8},{\"x\":1685232571258,\"y\":13.67},{\"x\":1685232871256,\"y\":13.67},{\"x\":1685233172128,\"y\":13.34},{\"x\":1685233471266,\"y\":13.26},{\"x\":1685233771304,\"y\":13.55},{\"x\":1685234071280,\"y\":13.5},{\"x\":1685234371267,\"y\":13.55},{\"x\":1685234671308,\"y\":13.47},{\"x\":1685234971294,\"y\":13.47},{\"x\":1685235271259,\"y\":13.47},{\"x\":1685235573201,\"y\":13.47},{\"x\":1685235871295,\"y\":13.51},{\"x\":1685236171262,\"y\":13.65},{\"x\":1685236471308,\"y\":13.36},{\"x\":1685236772640,\"y\":13.36},{\"x\":1685237071263,\"y\":13.55},{\"x\":1685237371273,\"y\":13.46},{\"x\":1685237671265,\"y\":13.46},{\"x\":1685237972838,\"y\":13.08},{\"x\":1685238271265,\"y\":13.08},{\"x\":1685238571268,\"y\":13.08},{\"x\":1685238871275,\"y\":13.28},{\"x\":1685239172611,\"y\":13.41},{\"x\":1685239471288,\"y\":13.33},{\"x\":1685239771274,\"y\":13.31},{\"x\":1685240071325,\"y\":13.15},{\"x\":1685240372227,\"y\":13.07},{\"x\":1685240671318,\"y\":13.07},{\"x\":1685240971329,\"y\":13.07},{\"x\":1685241271288,\"y\":13.07},{\"x\":1685241571466,\"y\":13.07},{\"x\":1685241871279,\"y\":12.98},{\"x\":1685242171369,\"y\":13.28},{\"x\":1685242471346,\"y\":13.28},{\"x\":1685242771307,\"y\":13.28},{\"x\":1685243071278,\"y\":13.28},{\"x\":1685243371329,\"y\":13.28},{\"x\":1685243671340,\"y\":13.34},{\"x\":1685243971443,\"y\":13.34},{\"x\":1685244271334,\"y\":13.34},{\"x\":1685244571292,\"y\":13.34},{\"x\":1685244871333,\"y\":13.47},{\"x\":1685245171537,\"y\":13.69},{\"x\":1685245471361,\"y\":13.69},{\"x\":1685245771298,\"y\":13.69},{\"x\":1685246071304,\"y\":13.6},{\"x\":1685246371537,\"y\":13.65},{\"x\":1685246671321,\"y\":13.56},{\"x\":1685246971335,\"y\":13.65},{\"x\":1685247271382,\"y\":13.48},{\"x\":1685247571470,\"y\":13.48},{\"x\":1685247871384,\"y\":13.28},{\"x\":1685248171411,\"y\":13.28},{\"x\":1685248471353,\"y\":13.24},{\"x\":1685248771403,\"y\":13.24},{\"x\":1685249071402,\"y\":13.23},{\"x\":1685249371376,\"y\":13.23},{\"x\":1685249671393,\"y\":13.23},{\"x\":1685249971493,\"y\":13.17},{\"x\":1685250271426,\"y\":13.08},{\"x\":1685250571467,\"y\":13.11},{\"x\":1685250871415,\"y\":13.14},{\"x\":1685251171439,\"y\":13.14},{\"x\":1685251471413,\"y\":13.14},{\"x\":1685251771409,\"y\":13.14},{\"x\":1685252071420,\"y\":13.19},{\"x\":1685252371615,\"y\":13.25},{\"x\":1685252671425,\"y\":13.17},{\"x\":1685252971434,\"y\":13.18},{\"x\":1685253271482,\"y\":13.38},{\"x\":1685253571596,\"y\":13.3},{\"x\":1685253871453,\"y\":13.38},{\"x\":1685254171462,\"y\":13.45},{\"x\":1685254471470,\"y\":13.51},{\"x\":1685254771721,\"y\":13.58},{\"x\":1685255071473,\"y\":13.58},{\"x\":1685255371478,\"y\":13.58},{\"x\":1685255671578,\"y\":13.78},{\"x\":1685255971776,\"y\":13.54},{\"x\":1685256271512,\"y\":13.78},{\"x\":1685256571498,\"y\":13.76},{\"x\":1685256871502,\"y\":13.87},{\"x\":1685257171620,\"y\":13.87},{\"x\":1685257471509,\"y\":14.11},{\"x\":1685257771511,\"y\":14.36},{\"x\":1685258071508,\"y\":14.36},{\"x\":1685258371764,\"y\":14.39},{\"x\":1685258671517,\"y\":14.56},{\"x\":1685258971516,\"y\":14.56},{\"x\":1685259271515,\"y\":14.92},{\"x\":1685259571742,\"y\":14.92},{\"x\":1685259871562,\"y\":15.01},{\"x\":1685260171531,\"y\":15.01},{\"x\":1685260471534,\"y\":15.01},{\"x\":1685260771696,\"y\":14.85},{\"x\":1685261071539,\"y\":15.04},{\"x\":1685261371589,\"y\":15.04},{\"x\":1685261671538,\"y\":15.4},{\"x\":1685261971867,\"y\":15.59},{\"x\":1685262271550,\"y\":15.59},{\"x\":1685262571548,\"y\":15.59},{\"x\":1685262871567,\"y\":15.46},{\"x\":1685263171794,\"y\":15.73},{\"x\":1685263471562,\"y\":15.69},{\"x\":1685263771602,\"y\":15.85},{\"x\":1685264071565,\"y\":15.71},{\"x\":1685264371846,\"y\":15.71},{\"x\":1685264671596,\"y\":15.72},{\"x\":1685264971615,\"y\":15.73},{\"x\":1685265271570,\"y\":15.73},{\"x\":1685265571596,\"y\":15.65},{\"x\":1685265871585,\"y\":15.47},{\"x\":1685266171591,\"y\":15.47},{\"x\":1685266471584,\"y\":15.48},{\"x\":1685266771720,\"y\":15.72},{\"x\":1685267071581,\"y\":15.72},{\"x\":1685267371587,\"y\":15.9},{\"x\":1685267671638,\"y\":16.17},{\"x\":1685267971896,\"y\":16.17},{\"x\":1685268271589,\"y\":15.98},{\"x\":1685268571603,\"y\":16.37},{\"x\":1685268871604,\"y\":16.37},{\"x\":1685269171897,\"y\":16.65},{\"x\":1685269471607,\"y\":16.58},{\"x\":1685269771613,\"y\":16.58},{\"x\":1685270071662,\"y\":16.41},{\"x\":1685270371882,\"y\":16.41},{\"x\":1685270671617,\"y\":16.41},{\"x\":1685270971621,\"y\":16.28},{\"x\":1685271271686,\"y\":16.19},{\"x\":1685271571713,\"y\":16.22},{\"x\":1685271871621,\"y\":16.15},{\"x\":1685272171630,\"y\":16.22},{\"x\":1685272471630,\"y\":15.92},{\"x\":1685272771817,\"y\":15.96},{\"x\":1685273071635,\"y\":15.96},{\"x\":1685273371650,\"y\":16.12},{\"x\":1685273671673,\"y\":15.95},{\"x\":1685273971701,\"y\":16.31},{\"x\":1685274271656,\"y\":16.31},{\"x\":1685274571692,\"y\":16.32},{\"x\":1685274871686,\"y\":16.32},{\"x\":1685275171921,\"y\":16.73},{\"x\":1685275471665,\"y\":16.65},{\"x\":1685275771698,\"y\":16.73},{\"x\":1685276071659,\"y\":16.22},{\"x\":1685276371756,\"y\":16.22},{\"x\":1685276671662,\"y\":16.48},{\"x\":1685276971675,\"y\":15.89},{\"x\":1685277271661,\"y\":15.89},{\"x\":1685277571970,\"y\":15.63},{\"x\":1685277871714,\"y\":15.68},{\"x\":1685278171675,\"y\":15.63},{\"x\":1685278471667,\"y\":15.56},{\"x\":1685278771920,\"y\":15.65},{\"x\":1685279071689,\"y\":15.87},{\"x\":1685279371671,\"y\":15.87},{\"x\":1685279671675,\"y\":15.87},{\"x\":1685279972052,\"y\":15.71},{\"x\":1685280271668,\"y\":15.71},{\"x\":1685280571670,\"y\":16.04},{\"x\":1685280871676,\"y\":16.04},{\"x\":1685281671888,\"y\":15.97},{\"x\":1685284096032,\"y\":15.5},{\"x\":1685284395921,\"y\":15.5},{\"x\":1685284695933,\"y\":15.5},{\"x\":1685284995930,\"y\":15.47},{\"x\":1685285295935,\"y\":15.47},{\"x\":1685285595944,\"y\":15.8},{\"x\":1685285895939,\"y\":15.8},{\"x\":1685286195949,\"y\":15.51},{\"x\":1685286495951,\"y\":15.51},{\"x\":1685286796009,\"y\":15.36},{\"x\":1685287095965,\"y\":15.27},{\"x\":1685287395953,\"y\":15.27},{\"x\":1685287695970,\"y\":15.36},{\"x\":1685287995955,\"y\":15.36},{\"x\":1685288295961,\"y\":15.34},{\"x\":1685288595996,\"y\":14.97},{\"x\":1685288895968,\"y\":14.97},{\"x\":1685289196133,\"y\":14.79},{\"x\":1685289495999,\"y\":14.79},{\"x\":1685289795972,\"y\":14.69},{\"x\":1685290095973,\"y\":14.69},{\"x\":1685290395973,\"y\":14.69},{\"x\":1685290695981,\"y\":14.79},{\"x\":1685290996003,\"y\":14.79},{\"x\":1685291295989,\"y\":14.79},{\"x\":1685291595985,\"y\":14.77},{\"x\":1685291896008,\"y\":14.5},{\"x\":1685292196008,\"y\":14.5},{\"x\":1685292496041,\"y\":14.55},{\"x\":1685292796005,\"y\":14.47},{\"x\":1685293096013,\"y\":14.55},{\"x\":1685293396082,\"y\":14.49},{\"x\":1685293696011,\"y\":14.45},{\"x\":1685293996026,\"y\":14.65},{\"x\":1685294296016,\"y\":14.65},{\"x\":1685294596031,\"y\":14.43},{\"x\":1685294896026,\"y\":14.43},{\"x\":1685295196031,\"y\":14.42},{\"x\":1685295496032,\"y\":14.4},{\"x\":1685295796073,\"y\":14.53},{\"x\":1685296096031,\"y\":14.53},{\"x\":1685296396105,\"y\":14.32},{\"x\":1685296696030,\"y\":14.24},{\"x\":1685296996047,\"y\":14.24},{\"x\":1685297296080,\"y\":14.48},{\"x\":1685297596038,\"y\":14.4},{\"x\":1685297896041,\"y\":14.48},{\"x\":1685298196059,\"y\":14.08},{\"x\":1685298496041,\"y\":14.08},{\"x\":1685298796049,\"y\":14.33},{\"x\":1685299096058,\"y\":14},{\"x\":1685299396061,\"y\":13.59},{\"x\":1685299696048,\"y\":13.56},{\"x\":1685299996049,\"y\":13.56},{\"x\":1685300296111,\"y\":13.8},{\"x\":1685300596061,\"y\":13.71},{\"x\":1685300896068,\"y\":13.71},{\"x\":1685301196056,\"y\":13.49},{\"x\":1685301496064,\"y\":13.49},{\"x\":1685301796076,\"y\":13.49},{\"x\":1685302096103,\"y\":13.4},{\"x\":1685302396063,\"y\":13.46},{\"x\":1685302696077,\"y\":13.34},{\"x\":1685302996145,\"y\":13.04},{\"x\":1685303296067,\"y\":12.95},{\"x\":1685303596073,\"y\":13.04},{\"x\":1685303896072,\"y\":12.69},{\"x\":1685304196079,\"y\":12.67},{\"x\":1685304496103,\"y\":12.49},{\"x\":1685304796082,\"y\":12.49},{\"x\":1685305096071,\"y\":12.49},{\"x\":1685305396094,\"y\":12.49},{\"x\":1685305696124,\"y\":12.2},{\"x\":1685305996075,\"y\":12.14},{\"x\":1685306296121,\"y\":11.8},{\"x\":1685306596072,\"y\":11.8},{\"x\":1685306896081,\"y\":11.8},{\"x\":1685307196124,\"y\":11.34},{\"x\":1685307496091,\"y\":11.34},{\"x\":1685307797175,\"y\":11.28},{\"x\":1685308096131,\"y\":11.3},{\"x\":1685308396095,\"y\":11.28},{\"x\":1685308696095,\"y\":11.3},{\"x\":1685308996098,\"y\":11.22},{\"x\":1685309296119,\"y\":11},{\"x\":1685309596104,\"y\":11},{\"x\":1685309896135,\"y\":10.5},{\"x\":1685310196123,\"y\":10.5},{\"x\":1685310496134,\"y\":10.5},{\"x\":1685310796104,\"y\":10.45},{\"x\":1685311096115,\"y\":10.45},{\"x\":1685311396137,\"y\":10.24},{\"x\":1685311696121,\"y\":10.24},{\"x\":1685311996202,\"y\":10.21},{\"x\":1685312296125,\"y\":10.21},{\"x\":1685312596146,\"y\":10.21},{\"x\":1685312896148,\"y\":9.93},{\"x\":1685313196132,\"y\":10.07},{\"x\":1685313496203,\"y\":9.89},{\"x\":1685313796150,\"y\":9.83},{\"x\":1685314096147,\"y\":9.75},{\"x\":1685314396195,\"y\":9.81},{\"x\":1685314696202,\"y\":9.81},{\"x\":1685314996169,\"y\":9.68},{\"x\":1685315296183,\"y\":9.65},{\"x\":1685315596155,\"y\":9.37},{\"x\":1685315896207,\"y\":9.43},{\"x\":1685316196167,\"y\":9.43},{\"x\":1685316496181,\"y\":9.32},{\"x\":1685316796216,\"y\":9.22},{\"x\":1685317096164,\"y\":9.22},{\"x\":1685317396213,\"y\":8.84},{\"x\":1685317696170,\"y\":8.76},{\"x\":1685317996185,\"y\":8.81},{\"x\":1685318296172,\"y\":8.72},{\"x\":1685318596191,\"y\":8.81},{\"x\":1685318896205,\"y\":8.99},{\"x\":1685319196165,\"y\":8.99},{\"x\":1685319496187,\"y\":8.9},{\"x\":1685319796169,\"y\":8.9},{\"x\":1685320096204,\"y\":8.99},{\"x\":1685320396170,\"y\":8.7},{\"x\":1685320696174,\"y\":8.65},{\"x\":1685320996175,\"y\":8.65},{\"x\":1685321296205,\"y\":8.41},{\"x\":1685321596177,\"y\":8.41},{\"x\":1685321896203,\"y\":8.41},{\"x\":1685322196246,\"y\":8.09},{\"x\":1685322496225,\"y\":7.88},{\"x\":1685322796181,\"y\":7.88},{\"x\":1685323096176,\"y\":7.73},{\"x\":1685323396175,\"y\":7.73},{\"x\":1685323696184,\"y\":7.67},{\"x\":1685323996186,\"y\":7.67},{\"x\":1685324296221,\"y\":7.83},{\"x\":1685324596190,\"y\":7.74},{\"x\":1685324896222,\"y\":7.83},{\"x\":1685325196203,\"y\":7.74},{\"x\":1685325496188,\"y\":7.32},{\"x\":1685325796536,\"y\":7.32},{\"x\":1685326096185,\"y\":7.32},{\"x\":1685326396231,\"y\":7.41},{\"x\":1685326696191,\"y\":7.41},{\"x\":1685326996194,\"y\":7.41},{\"x\":1685327296195,\"y\":7.29},{\"x\":1685327596190,\"y\":7.3},{\"x\":1685327896227,\"y\":7.33},{\"x\":1685328196279,\"y\":7.33},{\"x\":1685328496276,\"y\":7.33},{\"x\":1685328796240,\"y\":7.2},{\"x\":1685329096191,\"y\":7.2},{\"x\":1685329396192,\"y\":7.11},{\"x\":1685329696248,\"y\":7.2},{\"x\":1685329996247,\"y\":7.2},{\"x\":1685330296206,\"y\":7.2},{\"x\":1685330596222,\"y\":7.11},{\"x\":1685330896241,\"y\":7.37},{\"x\":1685331196223,\"y\":7.37},{\"x\":1685331496227,\"y\":7.37},{\"x\":1685331796287,\"y\":7.54},{\"x\":1685332096241,\"y\":7.54},{\"x\":1685332396262,\"y\":7.25},{\"x\":1685332696288,\"y\":7.34},{\"x\":1685332996282,\"y\":7.25},{\"x\":1685333296250,\"y\":7.34},{\"x\":1685333596314,\"y\":7.3},{\"x\":1685333896248,\"y\":7.3},{\"x\":1685334196250,\"y\":7.22},{\"x\":1685334496287,\"y\":7.3},{\"x\":1685334796262,\"y\":7.3},{\"x\":1685335096310,\"y\":7.34},{\"x\":1685335396322,\"y\":7.34},{\"x\":1685335696328,\"y\":7.35},{\"x\":1685335996277,\"y\":7.35},{\"x\":1685336296341,\"y\":7.46},{\"x\":1685336596305,\"y\":7.46},{\"x\":1685336896288,\"y\":7.46},{\"x\":1685337196337,\"y\":7.77},{\"x\":1685337496307,\"y\":7.68},{\"x\":1685337796344,\"y\":7.77},{\"x\":1685338096310,\"y\":7.7},{\"x\":1685338396309,\"y\":7.7},{\"x\":1685338696313,\"y\":7.87},{\"x\":1685338996338,\"y\":7.91},{\"x\":1685339296305,\"y\":8.24},{\"x\":1685339596306,\"y\":8.24},{\"x\":1685339896363,\"y\":8.87},{\"x\":1685340196325,\"y\":8.81},{\"x\":1685340496328,\"y\":8.87},{\"x\":1685340796324,\"y\":8.93},{\"x\":1685341096323,\"y\":8.93},{\"x\":1685341396403,\"y\":9.54},{\"x\":1685341696331,\"y\":9.46},{\"x\":1685341996329,\"y\":9.48},{\"x\":1685342296334,\"y\":9.53},{\"x\":1685342596385,\"y\":9.94},{\"x\":1685342896330,\"y\":9.94},{\"x\":1685343196338,\"y\":10.15},{\"x\":1685343496342,\"y\":10.74},{\"x\":1685343796354,\"y\":10.74},{\"x\":1685344096339,\"y\":11.17},{\"x\":1685344396346,\"y\":11.17},{\"x\":1685344697407,\"y\":11.29},{\"x\":1685344996353,\"y\":11.26},{\"x\":1685345296424,\"y\":11.74},{\"x\":1685345596406,\"y\":11.65},{\"x\":1685345896351,\"y\":11.65},{\"x\":1685346196416,\"y\":12.07},{\"x\":1685346496410,\"y\":12.16},{\"x\":1685346796378,\"y\":12.07},{\"x\":1685347096406,\"y\":12.03},{\"x\":1685347396359,\"y\":12.03},{\"x\":1685347696378,\"y\":12.03},{\"x\":1685347996394,\"y\":12.1},{\"x\":1685348296366,\"y\":12.1},{\"x\":1685348596360,\"y\":12.02},{\"x\":1685348896369,\"y\":12.44},{\"x\":1685349196480,\"y\":12.49},{\"x\":1685349496365,\"y\":12.49},{\"x\":1685349796376,\"y\":12.49},{\"x\":1685350096394,\"y\":12.97},{\"x\":1685350396369,\"y\":12.97},{\"x\":1685350696451,\"y\":13.05},{\"x\":1685350996421,\"y\":13.19},{\"x\":1685351296370,\"y\":13.05},{\"x\":1685351596436,\"y\":13.25},{\"x\":1685351896375,\"y\":13.25},{\"x\":1685352196381,\"y\":13.25},{\"x\":1685352496379,\"y\":13.28},{\"x\":1685352796390,\"y\":13.28},{\"x\":1685353096503,\"y\":13.97},{\"x\":1685353396412,\"y\":13.97},{\"x\":1685353696392,\"y\":13.97},{\"x\":1685353996429,\"y\":14.15},{\"x\":1685354296437,\"y\":14.15},{\"x\":1685354596450,\"y\":14.15},{\"x\":1685354896393,\"y\":14.3},{\"x\":1685355196401,\"y\":14.3},{\"x\":1685355496440,\"y\":14.36},{\"x\":1685355796402,\"y\":14.36},{\"x\":1685356096449,\"y\":14.32},{\"x\":1685356396391,\"y\":14.24},{\"x\":1685356696401,\"y\":14.32},{\"x\":1685356996401,\"y\":14.61},{\"x\":1685357296408,\"y\":14.61},{\"x\":1685357596403,\"y\":15.01},{\"x\":1685357896429,\"y\":15.59},{\"x\":1685358196393,\"y\":15.59},{\"x\":1685358496468,\"y\":15.59},{\"x\":1685358796424,\"y\":16},{\"x\":1685359096400,\"y\":16},{\"x\":1685359396448,\"y\":16.11},{\"x\":1685359696410,\"y\":16.08},{\"x\":1685359996404,\"y\":16.08},{\"x\":1685360296411,\"y\":16.35},{\"x\":1685360596442,\"y\":16.35},{\"x\":1685360896460,\"y\":16.44},{\"x\":1685361196442,\"y\":16.44},{\"x\":1685361496409,\"y\":16.44},{\"x\":1685361796427,\"y\":16.52},{\"x\":1685362096416,\"y\":16.62},{\"x\":1685362396413,\"y\":16.67},{\"x\":1685362696411,\"y\":16.68},{\"x\":1685362996461,\"y\":16.79},{\"x\":1685363296411,\"y\":16.85},{\"x\":1685363596420,\"y\":16.79},{\"x\":1685363896467,\"y\":16.76},{\"x\":1685364196435,\"y\":16.76},{\"x\":1685364496434,\"y\":16.76},{\"x\":1685364796430,\"y\":16.68},{\"x\":1685365096423,\"y\":17.1},{\"x\":1685365396432,\"y\":17.1},{\"x\":1685365696497,\"y\":17.46},{\"x\":1685365996459,\"y\":17.44},{\"x\":1685366296423,\"y\":17.46},{\"x\":1685366596445,\"y\":17.44},{\"x\":1685366896437,\"y\":17.38},{\"x\":1685367196433,\"y\":17.38},{\"x\":1685367496432,\"y\":17.38},{\"x\":1685367796435,\"y\":17.27},{\"x\":1685368096426,\"y\":17.27},{\"x\":1685368396429,\"y\":17.26},{\"x\":1685368696436,\"y\":17.26},{\"x\":1685368996438,\"y\":17.44},{\"x\":1685369296428,\"y\":17.32},{\"x\":1685369596501,\"y\":17.5},{\"x\":1685369896441,\"y\":17.42},{\"x\":1685370196509,\"y\":17.5},{\"x\":1685370496485,\"y\":17.7},{\"x\":1685370796502,\"y\":17.7},{\"x\":1685371096455,\"y\":17.7},{\"x\":1685371396510,\"y\":17.39},{\"x\":1685371696485,\"y\":17.39},{\"x\":1685371996502,\"y\":17.64},{\"x\":1685372296476,\"y\":17.31},{\"x\":1685372596474,\"y\":17.78},{\"x\":1685372896482,\"y\":17.78},{\"x\":1685373196484,\"y\":17.78},{\"x\":1685373496572,\"y\":17.86},{\"x\":1685373796514,\"y\":17.78},{\"x\":1685374096510,\"y\":17.81},{\"x\":1685374396544,\"y\":17.86},{\"x\":1685374696519,\"y\":17.78},{\"x\":1685374996566,\"y\":17.78},{\"x\":1685375296542,\"y\":17.89},{\"x\":1685375596519,\"y\":17.89},{\"x\":1685375896531,\"y\":17.89},{\"x\":1685376196570,\"y\":17.89},{\"x\":1685376496581,\"y\":18.04},{\"x\":1685376796589,\"y\":18.04},{\"x\":1685377096547,\"y\":17.74},{\"x\":1685377396613,\"y\":17.74},{\"x\":1685377696596,\"y\":17.6},{\"x\":1685377996542,\"y\":17.51},{\"x\":1685378296565,\"y\":17.51},{\"x\":1685378596624,\"y\":17.51},{\"x\":1685378896627,\"y\":17.42},{\"x\":1685379196643,\"y\":17.2},{\"x\":1685379496586,\"y\":17.42},{\"x\":1685379796637,\"y\":17.09},{\"x\":1685380096596,\"y\":17.09},{\"x\":1685380396635,\"y\":17.11},{\"x\":1685380696607,\"y\":17.04},{\"x\":1685380996713,\"y\":17.08},{\"x\":1685381296598,\"y\":16.99},{\"x\":1685381596615,\"y\":17.08},{\"x\":1685381896608,\"y\":17.03},{\"x\":1685382196647,\"y\":17.03},{\"x\":1685382496621,\"y\":16.09},{\"x\":1685382796622,\"y\":16.25},{\"x\":1685383096636,\"y\":16.25},{\"x\":1685383396656,\"y\":16.25},{\"x\":1685383696677,\"y\":16.1},{\"x\":1685383996640,\"y\":16.09},{\"x\":1685384296634,\"y\":16.09},{\"x\":1685384596702,\"y\":16.09},{\"x\":1685384896755,\"y\":15.72},{\"x\":1685385196657,\"y\":15.7},{\"x\":1685385496648,\"y\":15.72},{\"x\":1685385796721,\"y\":14.94},{\"x\":1685386096661,\"y\":14.89},{\"x\":1685386396764,\"y\":14.48},{\"x\":1685386696644,\"y\":14.48},{\"x\":1685386996711,\"y\":14.39},{\"x\":1685387296665,\"y\":14.47},{\"x\":1685387596687,\"y\":14.47},{\"x\":1685387896719,\"y\":14.45},{\"x\":1685388196746,\"y\":14.37},{\"x\":1685388496677,\"y\":14.36},{\"x\":1685388796728,\"y\":14.16},{\"x\":1685389096728,\"y\":14.95},{\"x\":1685389396772,\"y\":15.89},{\"x\":1685389696700,\"y\":15.8},{\"x\":1685389996694,\"y\":15.8},{\"x\":1685390296741,\"y\":13.52},{\"x\":1685390596769,\"y\":13.52},{\"x\":1685390896706,\"y\":13.52},{\"x\":1685391196708,\"y\":12.38},{\"x\":1685391496778,\"y\":12.52},{\"x\":1685391796848,\"y\":12.52},{\"x\":1685392096805,\"y\":12.37},{\"x\":1685392396723,\"y\":12.01},{\"x\":1685392696723,\"y\":12.01},{\"x\":1685392996799,\"y\":11.33},{\"x\":1685393296731,\"y\":11.58},{\"x\":1685393596725,\"y\":11.33},{\"x\":1685393896759,\"y\":11.64},{\"x\":1685394196778,\"y\":11.64},{\"x\":1685394496727,\"y\":11.63},{\"x\":1685394796838,\"y\":11},{\"x\":1685395096733,\"y\":10.92},{\"x\":1685395396756,\"y\":10.84},{\"x\":1685395696744,\"y\":10.81},{\"x\":1685395996738,\"y\":10.81},{\"x\":1685396296745,\"y\":10.81},{\"x\":1685396596863,\"y\":9.92},{\"x\":1685396896748,\"y\":9.92},{\"x\":1685397196744,\"y\":9.92},{\"x\":1685397496752,\"y\":9.84},{\"x\":1685397797031,\"y\":9.29},{\"x\":1685398096745,\"y\":9.59},{\"x\":1685398396778,\"y\":9.29},{\"x\":1685398696753,\"y\":9.67},{\"x\":1685398996882,\"y\":9.8},{\"x\":1685399296749,\"y\":9.67},{\"x\":1685399596872,\"y\":9.64},{\"x\":1685399896856,\"y\":9.29},{\"x\":1685400196853,\"y\":9.29},{\"x\":1685400496758,\"y\":9.29},{\"x\":1685400796806,\"y\":8.88},{\"x\":1685401096749,\"y\":8.88},{\"x\":1685401396853,\"y\":8.88},{\"x\":1685401696823,\"y\":8.87},{\"x\":1685401996749,\"y\":8.93},{\"x\":1685402296834,\"y\":8.83},{\"x\":1685402596862,\"y\":8.83},{\"x\":1685402896766,\"y\":8.83},{\"x\":1685403196764,\"y\":8.69},{\"x\":1685403496778,\"y\":8.79},{\"x\":1685403796902,\"y\":8.76},{\"x\":1685404096775,\"y\":8.76},{\"x\":1685404396829,\"y\":8.45},{\"x\":1685404696790,\"y\":8.45},{\"x\":1685404996816,\"y\":8.36},{\"x\":1685405296883,\"y\":8.71},{\"x\":1685405596870,\"y\":8.7},{\"x\":1685405896793,\"y\":8.63},{\"x\":1685406196983,\"y\":8.7},{\"x\":1685406496794,\"y\":8.47},{\"x\":1685406796799,\"y\":8.47},{\"x\":1685407096806,\"y\":8.41},{\"x\":1685407396889,\"y\":8.27},{\"x\":1685407696814,\"y\":8.19},{\"x\":1685407996818,\"y\":8.27},{\"x\":1685408296823,\"y\":8.19},{\"x\":1685408596913,\"y\":8.19},{\"x\":1685408896867,\"y\":7.82},{\"x\":1685409196827,\"y\":7.82},{\"x\":1685409496885,\"y\":7.78},{\"x\":1685409796923,\"y\":7.69},{\"x\":1685410096833,\"y\":7.69},{\"x\":1685410396863,\"y\":7.68},{\"x\":1685410696853,\"y\":7.69},{\"x\":1685410996931,\"y\":7.59},{\"x\":1685411296850,\"y\":7.59},{\"x\":1685411596860,\"y\":7.59},{\"x\":1685411896863,\"y\":7.34},{\"x\":1685412197065,\"y\":7.41},{\"x\":1685412496865,\"y\":7.34},{\"x\":1685412796871,\"y\":7.34},{\"x\":1685413096914,\"y\":7.41},{\"x\":1685413396986,\"y\":7.41},{\"x\":1685413696881,\"y\":7.41},{\"x\":1685413996886,\"y\":7.35},{\"x\":1685414296923,\"y\":7.82},{\"x\":1685414597013,\"y\":7.95},{\"x\":1685414896976,\"y\":7.95},{\"x\":1685415196895,\"y\":7.95},{\"x\":1685415496889,\"y\":7.95},{\"x\":1685415796967,\"y\":8.17},{\"x\":1685416096899,\"y\":8.17},{\"x\":1685416396952,\"y\":8.17},{\"x\":1685416696900,\"y\":8.09},{\"x\":1685416997056,\"y\":8.23},{\"x\":1685417296914,\"y\":8.21},{\"x\":1685417596918,\"y\":8.23},{\"x\":1685417896941,\"y\":8.11},{\"x\":1685418197072,\"y\":8.11},{\"x\":1685418496972,\"y\":7.78},{\"x\":1685418796976,\"y\":7.78},{\"x\":1685419096917,\"y\":7.78},{\"x\":1685419397122,\"y\":7.76},{\"x\":1685419696925,\"y\":7.7},{\"x\":1685419996981,\"y\":7.76},{\"x\":1685420296927,\"y\":7.98},{\"x\":1685420597159,\"y\":7.68},{\"x\":1685420896981,\"y\":8.25},{\"x\":1685421196938,\"y\":8.25},{\"x\":1685421496968,\"y\":9.25},{\"x\":1685421797058,\"y\":8.95},{\"x\":1685422096935,\"y\":8.95},{\"x\":1685422397004,\"y\":9.27},{\"x\":1685422696956,\"y\":9.27},{\"x\":1685422997188,\"y\":9.29},{\"x\":1685423296955,\"y\":9.29},{\"x\":1685423597001,\"y\":9.11},{\"x\":1685423896956,\"y\":9.02},{\"x\":1685424197012,\"y\":9.02},{\"x\":1685424497000,\"y\":9.11},{\"x\":1685424796971,\"y\":9.02},{\"x\":1685425096964,\"y\":10.04},{\"x\":1685425397109,\"y\":10.04},{\"x\":1685425696964,\"y\":10.02},{\"x\":1685425996974,\"y\":9.99},{\"x\":1685426296965,\"y\":10.2},{\"x\":1685426597100,\"y\":10.28},{\"x\":1685426896989,\"y\":10.33},{\"x\":1685427196988,\"y\":10.33},{\"x\":1685427497031,\"y\":10.45},{\"x\":1685427797156,\"y\":10.38},{\"x\":1685428097083,\"y\":10.94},{\"x\":1685428396991,\"y\":10.94},{\"x\":1685428696996,\"y\":10.82},{\"x\":1685428997105,\"y\":11.01},{\"x\":1685429296992,\"y\":11.01},{\"x\":1685429597030,\"y\":11.01},{\"x\":1685429896994,\"y\":11.04},{\"x\":1685430197076,\"y\":11.04},{\"x\":1685430497010,\"y\":11.39},{\"x\":1685430796999,\"y\":11.6},{\"x\":1685431097007,\"y\":11.55},{\"x\":1685431397249,\"y\":11.81},{\"x\":1685431697002,\"y\":11.81},{\"x\":1685431997013,\"y\":11.81},{\"x\":1685432297009,\"y\":11.82},{\"x\":1685432597030,\"y\":11.82},{\"x\":1685432897132,\"y\":12.15},{\"x\":1685433197016,\"y\":12.07},{\"x\":1685433497009,\"y\":12.15},{\"x\":1685433797139,\"y\":12.16},{\"x\":1685434097016,\"y\":12.19},{\"x\":1685434397064,\"y\":12.22},{\"x\":1685434697027,\"y\":12.14},{\"x\":1685434997238,\"y\":12.14},{\"x\":1685435297077,\"y\":12.32},{\"x\":1685435597014,\"y\":12.33},{\"x\":1685435897071,\"y\":12.41},{\"x\":1685436197195,\"y\":12.35},{\"x\":1685436497013,\"y\":12.35},{\"x\":1685436797015,\"y\":12.32},{\"x\":1685437097050,\"y\":12.32},{\"x\":1685437397237,\"y\":12.33},{\"x\":1685437697030,\"y\":12.33},{\"x\":1685437997069,\"y\":12.27},{\"x\":1685438297034,\"y\":12.19},{\"x\":1685438597203,\"y\":12.17},{\"x\":1685438897033,\"y\":12.34},{\"x\":1685439197064,\"y\":12.31},{\"x\":1685439497033,\"y\":12.32},{\"x\":1685439797107,\"y\":12.31},{\"x\":1685440097051,\"y\":13.11},{\"x\":1685440397038,\"y\":13.21},{\"x\":1685440697073,\"y\":13.37},{\"x\":1685440997179,\"y\":13.19},{\"x\":1685441297048,\"y\":13.28},{\"x\":1685441597041,\"y\":13.19},{\"x\":1685441897039,\"y\":13.29},{\"x\":1685442197094,\"y\":13.29},{\"x\":1685442497030,\"y\":13.33},{\"x\":1685442797045,\"y\":13.33},{\"x\":1685443097030,\"y\":13.2},{\"x\":1685443397349,\"y\":13.31},{\"x\":1685443697027,\"y\":13.31},{\"x\":1685443997092,\"y\":13.23},{\"x\":1685444297033,\"y\":13.77},{\"x\":1685444597073,\"y\":13.68},{\"x\":1685444897078,\"y\":16.19},{\"x\":1685445197034,\"y\":16.19},{\"x\":1685445497068,\"y\":16.19},{\"x\":1685445797209,\"y\":16.19},{\"x\":1685446097058,\"y\":16.19},{\"x\":1685446397051,\"y\":17.22},{\"x\":1685446697059,\"y\":17.22},{\"x\":1685446997166,\"y\":18.41},{\"x\":1685447297115,\"y\":18.41},{\"x\":1685447597112,\"y\":18.41},{\"x\":1685447897089,\"y\":18.41},{\"x\":1685448197159,\"y\":12.74},{\"x\":1685448497098,\"y\":12.72},{\"x\":1685448797127,\"y\":12.89},{\"x\":1685449097105,\"y\":12.97},{\"x\":1685449397373,\"y\":13.15},{\"x\":1685449697109,\"y\":13.03},{\"x\":1685449997136,\"y\":13.06},{\"x\":1685450297128,\"y\":13.06},{\"x\":1685450597387,\"y\":13.06},{\"x\":1685450897183,\"y\":13.21},{\"x\":1685451197144,\"y\":13.21},{\"x\":1685451497142,\"y\":13.33},{\"x\":1685451797185,\"y\":13.33},{\"x\":1685452097148,\"y\":13.57},{\"x\":1685452397249,\"y\":13.64},{\"x\":1685452697161,\"y\":13.64},{\"x\":1685452997474,\"y\":13.64},{\"x\":1685453297206,\"y\":13.97},{\"x\":1685453597173,\"y\":13.69},{\"x\":1685453897183,\"y\":13.94},{\"x\":1685454197215,\"y\":13.94},{\"x\":1685454497192,\"y\":14.16},{\"x\":1685454797260,\"y\":14.48},{\"x\":1685455097202,\"y\":14.67},{\"x\":1685455397476,\"y\":14.67},{\"x\":1685455697209,\"y\":14.86},{\"x\":1685455997240,\"y\":15.03},{\"x\":1685456297262,\"y\":15.18},{\"x\":1685456597249,\"y\":15.18},{\"x\":1685456897228,\"y\":15.09},{\"x\":1685457197269,\"y\":15.55},{\"x\":1685457497236,\"y\":15.55},{\"x\":1685457797465,\"y\":15.55},{\"x\":1685458097286,\"y\":16.03},{\"x\":1685458397242,\"y\":16.03},{\"x\":1685458697267,\"y\":16.03},{\"x\":1685458997286,\"y\":15.84},{\"x\":1685459297248,\"y\":15.84},{\"x\":1685459597268,\"y\":16.22},{\"x\":1685459897263,\"y\":16.22},{\"x\":1685460197357,\"y\":16.94},{\"x\":1685460497301,\"y\":16.94},{\"x\":1685460797266,\"y\":16.94},{\"x\":1685461097305,\"y\":16.95},{\"x\":1685461397544,\"y\":17.05},{\"x\":1685461697280,\"y\":16.95},{\"x\":1685461997291,\"y\":16.97},{\"x\":1685462297287,\"y\":16.97},{\"x\":1685462597465,\"y\":17.35},{\"x\":1685462897303,\"y\":17.35},{\"x\":1685463197366,\"y\":17.2},{\"x\":1685463737229,\"y\":17.2},{\"x\":1685464037277,\"y\":17.14},{\"x\":1685464337245,\"y\":17.14},{\"x\":1685464637256,\"y\":17.03},{\"x\":1685464937262,\"y\":17.14},{\"x\":1685465237316,\"y\":17.08},{\"x\":1685465537294,\"y\":17.08},{\"x\":1685465837334,\"y\":17.31},{\"x\":1685466137337,\"y\":17.31},{\"x\":1685466437310,\"y\":17.03},{\"x\":1685466737354,\"y\":17.04},{\"x\":1685467037407,\"y\":16.96},{\"x\":1685467337326,\"y\":17.16},{\"x\":1685467637328,\"y\":17.13},{\"x\":1685467937333,\"y\":16.81},{\"x\":1685468237339,\"y\":16.48},{\"x\":1685468537360,\"y\":16.48},{\"x\":1685468837368,\"y\":16.47},{\"x\":1685469137413,\"y\":15.9},{\"x\":1685469437370,\"y\":16.04},{\"x\":1685469737372,\"y\":15.9},{\"x\":1685470037427,\"y\":15.82},{\"x\":1685470337392,\"y\":15.82},{\"x\":1685470637406,\"y\":15.82},{\"x\":1685470937407,\"y\":15.69},{\"x\":1685471237416,\"y\":15.83},{\"x\":1685471537421,\"y\":15.83},{\"x\":1685471837423,\"y\":15.95},{\"x\":1685472137555,\"y\":15.16},{\"x\":1685472437434,\"y\":15.08},{\"x\":1685472737439,\"y\":14.87},{\"x\":1685473037727,\"y\":14.79},{\"x\":1685473337523,\"y\":15.1},{\"x\":1685473637452,\"y\":14.79},{\"x\":1685473937500,\"y\":14.51},{\"x\":1685474237504,\"y\":14.29},{\"x\":1685474537449,\"y\":14.29},{\"x\":1685474837471,\"y\":14.21},{\"x\":1685475137479,\"y\":14.06},{\"x\":1685475437511,\"y\":13.27},{\"x\":1685475737497,\"y\":13.27},{\"x\":1685476037496,\"y\":13.19},{\"x\":1685476337572,\"y\":13.61},{\"x\":1685476637511,\"y\":13.49},{\"x\":1685476937513,\"y\":13.47},{\"x\":1685477237528,\"y\":13.42},{\"x\":1685477537526,\"y\":13.44},{\"x\":1685477837530,\"y\":13.44},{\"x\":1685478137582,\"y\":13.03},{\"x\":1685478437532,\"y\":13.14},{\"x\":1685478737565,\"y\":12.83},{\"x\":1685479037545,\"y\":12.91},{\"x\":1685479337555,\"y\":12.59},{\"x\":1685479637566,\"y\":12.56},{\"x\":1685479937595,\"y\":12.5},{\"x\":1685480237565,\"y\":12.61},{\"x\":1685480537635,\"y\":12.43},{\"x\":1685480837564,\"y\":12.43},{\"x\":1685481137578,\"y\":12.27},{\"x\":1685481437654,\"y\":12.32},{\"x\":1685481737583,\"y\":12.24},{\"x\":1685482037634,\"y\":12.13},{\"x\":1685482337596,\"y\":12.13},{\"x\":1685482637588,\"y\":12.04},{\"x\":1685482937605,\"y\":11.88},{\"x\":1685483237606,\"y\":11.88},{\"x\":1685483537617,\"y\":11.87},{\"x\":1685483837694,\"y\":11.87},{\"x\":1685484137652,\"y\":11.83},{\"x\":1685484437620,\"y\":11.55},{\"x\":1685484737629,\"y\":11.83},{\"x\":1685485037682,\"y\":11.43},{\"x\":1685485337673,\"y\":11.43},{\"x\":1685485637637,\"y\":11.43},{\"x\":1685485937698,\"y\":11.4},{\"x\":1685486237666,\"y\":11.4},{\"x\":1685486537695,\"y\":11.4},{\"x\":1685486837701,\"y\":11.34},{\"x\":1685487137670,\"y\":11.34},{\"x\":1685487437690,\"y\":11.34},{\"x\":1685487737711,\"y\":11.08},{\"x\":1685488037671,\"y\":11.08},{\"x\":1685488337728,\"y\":11.08},{\"x\":1685488637752,\"y\":11.06},{\"x\":1685488937679,\"y\":11.06},{\"x\":1685489237735,\"y\":11.04},{\"x\":1685489537691,\"y\":10.96},{\"x\":1685489837848,\"y\":11.05},{\"x\":1685490137701,\"y\":11.05},{\"x\":1685490437705,\"y\":10.95},{\"x\":1685490737730,\"y\":10.86},{\"x\":1685491037756,\"y\":10.8},{\"x\":1685491337708,\"y\":10.8},{\"x\":1685491637705,\"y\":10.8},{\"x\":1685491937711,\"y\":10.71},{\"x\":1685492237764,\"y\":10.8},{\"x\":1685492537769,\"y\":10.8},{\"x\":1685492837718,\"y\":10.8},{\"x\":1685493137753,\"y\":10.8},{\"x\":1685493437930,\"y\":10.87},{\"x\":1685493737724,\"y\":10.87},{\"x\":1685494037777,\"y\":10.61},{\"x\":1685494337747,\"y\":10.52},{\"x\":1685494637852,\"y\":10.61},{\"x\":1685494937737,\"y\":10.61},{\"x\":1685495237777,\"y\":10.63},{\"x\":1685495537775,\"y\":10.62},{\"x\":1685495837812,\"y\":10.51},{\"x\":1685496137755,\"y\":10.43},{\"x\":1685496437796,\"y\":10.49},{\"x\":1685496737809,\"y\":10.49},{\"x\":1685497037761,\"y\":10.49},{\"x\":1685497337761,\"y\":10.23},{\"x\":1685497637765,\"y\":10.23},{\"x\":1685497937814,\"y\":10.31},{\"x\":1685498237807,\"y\":10.31},{\"x\":1685498537788,\"y\":10.31},{\"x\":1685498837775,\"y\":10.23},{\"x\":1685499137789,\"y\":10.23},{\"x\":1685499437805,\"y\":10.23},{\"x\":1685499737817,\"y\":10.31},{\"x\":1685500037780,\"y\":10.31},{\"x\":1685500337844,\"y\":10.14},{\"x\":1685500637891,\"y\":10.14},{\"x\":1685500937793,\"y\":10.14},{\"x\":1685501237793,\"y\":10.14},{\"x\":1685501537807,\"y\":10.05},{\"x\":1685501837925,\"y\":10.05},{\"x\":1685502137801,\"y\":10.05},{\"x\":1685502437811,\"y\":9.97},{\"x\":1685502737895,\"y\":10.14},{\"x\":1685503037900,\"y\":10.16},{\"x\":1685503337808,\"y\":10.16},{\"x\":1685503637807,\"y\":10.08},{\"x\":1685503937812,\"y\":9.94},{\"x\":1685504237850,\"y\":9.94},{\"x\":1685504537822,\"y\":9.99},{\"x\":1685504837872,\"y\":9.96},{\"x\":1685505137866,\"y\":9.98},{\"x\":1685505438032,\"y\":9.98},{\"x\":1685505737834,\"y\":9.98},{\"x\":1685506037879,\"y\":9.81},{\"x\":1685506337861,\"y\":9.81},{\"x\":1685506637914,\"y\":9.81},{\"x\":1685506937872,\"y\":9.81},{\"x\":1685507237889,\"y\":9.81},{\"x\":1685507537838,\"y\":9.81},{\"x\":1685507837888,\"y\":9.81},{\"x\":1685508137887,\"y\":9.82},{\"x\":1685508437832,\"y\":9.73},{\"x\":1685508737867,\"y\":9.82},{\"x\":1685509037974,\"y\":9.62},{\"x\":1685509337857,\"y\":9.82},{\"x\":1685509637848,\"y\":9.62},{\"x\":1685509937889,\"y\":9.62},{\"x\":1685510237947,\"y\":9.62},{\"x\":1685510537889,\"y\":9.65},{\"x\":1685510837852,\"y\":9.65},{\"x\":1685511137855,\"y\":9.65},{\"x\":1685511437965,\"y\":9.57},{\"x\":1685511737859,\"y\":9.57},{\"x\":1685512037848,\"y\":9.54},{\"x\":1685512337852,\"y\":9.55},{\"x\":1685512638025,\"y\":9.58},{\"x\":1685512937857,\"y\":9.57},{\"x\":1685513237850,\"y\":9.58},{\"x\":1685513537848,\"y\":9.57},{\"x\":1685513837893,\"y\":9.57},{\"x\":1685514137857,\"y\":9.75},{\"x\":1685514437855,\"y\":9.75},{\"x\":1685514737977,\"y\":9.84},{\"x\":1685515037889,\"y\":9.84},{\"x\":1685515337874,\"y\":9.84},{\"x\":1685515637865,\"y\":9.84},{\"x\":1685515937961,\"y\":9.84},{\"x\":1685516238059,\"y\":9.84},{\"x\":1685516537872,\"y\":9.75},{\"x\":1685516837969,\"y\":10.01},{\"x\":1685517137902,\"y\":10.01},{\"x\":1685517437997,\"y\":10.01},{\"x\":1685517737887,\"y\":10.03},{\"x\":1685518037974,\"y\":10.03},{\"x\":1685518337895,\"y\":9.92},{\"x\":1685518637932,\"y\":9.92},{\"x\":1685518937904,\"y\":9.96},{\"x\":1685519238002,\"y\":10.1},{\"x\":1685519537959,\"y\":10.1},{\"x\":1685519837988,\"y\":10.1},{\"x\":1685520137912,\"y\":10.1},{\"x\":1685520437906,\"y\":10.37},{\"x\":1685520737913,\"y\":10.33},{\"x\":1685521038006,\"y\":10.33},{\"x\":1685521337908,\"y\":10.33},{\"x\":1685521637915,\"y\":10.33},{\"x\":1685521937963,\"y\":10.25},{\"x\":1685522238080,\"y\":10.54},{\"x\":1685522537924,\"y\":10.54},{\"x\":1685522837919,\"y\":10.54},{\"x\":1685523137944,\"y\":10.5},{\"x\":1685523438045,\"y\":10.6},{\"x\":1685523737930,\"y\":10.58},{\"x\":1685524037929,\"y\":10.52},{\"x\":1685524337974,\"y\":10.82},{\"x\":1685524638007,\"y\":10.82},{\"x\":1685524937934,\"y\":10.82},{\"x\":1685525237935,\"y\":10.95},{\"x\":1685525537943,\"y\":10.99},{\"x\":1685525838078,\"y\":11.08},{\"x\":1685526137951,\"y\":11.08},{\"x\":1685526437950,\"y\":10.99},{\"x\":1685526737944,\"y\":11.07},{\"x\":1685527038721,\"y\":11.07},{\"x\":1685527338012,\"y\":11.11},{\"x\":1685527637961,\"y\":11.11},{\"x\":1685527937952,\"y\":11.07},{\"x\":1685528238080,\"y\":11.16},{\"x\":1685528537978,\"y\":11.03},{\"x\":1685528837960,\"y\":11.12},{\"x\":1685529137967,\"y\":11.08},{\"x\":1685529438131,\"y\":11.61},{\"x\":1685529738016,\"y\":11.61},{\"x\":1685530037967,\"y\":11.61},{\"x\":1685530337963,\"y\":11.61},{\"x\":1685530638067,\"y\":11.61},{\"x\":1685530937995,\"y\":11.61},{\"x\":1685531237967,\"y\":11.61},{\"x\":1685531537979,\"y\":12.07},{\"x\":1685531838029,\"y\":11.73},{\"x\":1685532137972,\"y\":11.72},{\"x\":1685532437974,\"y\":11.73},{\"x\":1685532738070,\"y\":11.43},{\"x\":1685533038022,\"y\":11.92},{\"x\":1685533338038,\"y\":16.07},{\"x\":1685533637982,\"y\":16.07},{\"x\":1685533938045,\"y\":16.07},{\"x\":1685534238057,\"y\":16.07},{\"x\":1685534537980,\"y\":16.07},{\"x\":1685534838036,\"y\":12.17},{\"x\":1685535137993,\"y\":12.17},{\"x\":1685535438136,\"y\":12.17},{\"x\":1685535737984,\"y\":12.17},{\"x\":1685536037986,\"y\":12.17},{\"x\":1685536337985,\"y\":12.17},{\"x\":1685536638024,\"y\":11.9},{\"x\":1685536938025,\"y\":15.91},{\"x\":1685537237999,\"y\":15.91},{\"x\":1685537538002,\"y\":15.91},{\"x\":1685537838106,\"y\":15.91},{\"x\":1685538138007,\"y\":15.82},{\"x\":1685538438058,\"y\":13.28},{\"x\":1685538738048,\"y\":13.28},{\"x\":1685539038104,\"y\":13.28},{\"x\":1685539338007,\"y\":13.2},{\"x\":1685539638019,\"y\":13.2},{\"x\":1685539938003,\"y\":13.33},{\"x\":1685540238052,\"y\":13.2},{\"x\":1685540538039,\"y\":16.75},{\"x\":1685540838017,\"y\":16.67},{\"x\":1685541138064,\"y\":16.75},{\"x\":1685541438054,\"y\":16.67},{\"x\":1685541738229,\"y\":17.3},{\"x\":1685542038012,\"y\":17.22},{\"x\":1685542338042,\"y\":17.3},{\"x\":1685542638126,\"y\":18.38},{\"x\":1685542938012,\"y\":18.38},{\"x\":1685543238056,\"y\":18.97},{\"x\":1685543538110,\"y\":17.86},{\"x\":1685543838052,\"y\":17.86},{\"x\":1685544138125,\"y\":17.78},{\"x\":1685544438027,\"y\":17.78},{\"x\":1685544738110,\"y\":14.96},{\"x\":1685545038075,\"y\":14.94},{\"x\":1685545338027,\"y\":14.94},{\"x\":1685545638075,\"y\":15.63},{\"x\":1685545938022,\"y\":15.63},{\"x\":1685546238174,\"y\":16},{\"x\":1685546538079,\"y\":15.83},{\"x\":1685546838020,\"y\":15.81},{\"x\":1685547138129,\"y\":14.9},{\"x\":1685547438095,\"y\":14.83},{\"x\":1685547738125,\"y\":14.9},{\"x\":1685548038046,\"y\":14.83},{\"x\":1685548338056,\"y\":14.9},{\"x\":1685548638087,\"y\":14.85},{\"x\":1685548938038,\"y\":14.85},{\"x\":1685549238040,\"y\":14.85},{\"x\":1685549538033,\"y\":14.97},{\"x\":1685549838168,\"y\":14.97},{\"x\":1685550138087,\"y\":15.03},{\"x\":1685550438030,\"y\":14.97},{\"x\":1685550738120,\"y\":14.3},{\"x\":1685551038139,\"y\":14.3},{\"x\":1685551338037,\"y\":13.6},{\"x\":1685551638033,\"y\":13.49},{\"x\":1685551938041,\"y\":13.47},{\"x\":1685552238174,\"y\":13.61},{\"x\":1685552538043,\"y\":13.61},{\"x\":1685552838043,\"y\":13.52},{\"x\":1685553138067,\"y\":13.52},{\"x\":1685553438546,\"y\":13.6},{\"x\":1685553738055,\"y\":13.52},{\"x\":1685554038140,\"y\":13.59},{\"x\":1685554338045,\"y\":13.51},{\"x\":1685554638134,\"y\":13.16},{\"x\":1685554938070,\"y\":13.16},{\"x\":1685555238057,\"y\":13.18},{\"x\":1685555538064,\"y\":13.07},{\"x\":1685555838093,\"y\":13.07},{\"x\":1685556138085,\"y\":13.14},{\"x\":1685556438188,\"y\":13.11},{\"x\":1685556738092,\"y\":13.12},{\"x\":1685557038192,\"y\":13.01},{\"x\":1685557338095,\"y\":13.01},{\"x\":1685557638182,\"y\":12.89},{\"x\":1685557938150,\"y\":12.8},{\"x\":1685558238172,\"y\":12.61},{\"x\":1685558538110,\"y\":12.8},{\"x\":1685558838201,\"y\":12.58},{\"x\":1685559138123,\"y\":12.58},{\"x\":1685559438206,\"y\":12.59},{\"x\":1685559738189,\"y\":12.57},{\"x\":1685560038129,\"y\":12.57},{\"x\":1685560338155,\"y\":12.57},{\"x\":1685560638404,\"y\":12.29},{\"x\":1685560938173,\"y\":12.29},{\"x\":1685561238169,\"y\":12.29},{\"x\":1685561538183,\"y\":12.17},{\"x\":1685561838237,\"y\":12.17},{\"x\":1685562138233,\"y\":12.21},{\"x\":1685562438249,\"y\":12.18},{\"x\":1685562738211,\"y\":12.18},{\"x\":1685563038228,\"y\":12.09},{\"x\":1685563338214,\"y\":12.07},{\"x\":1685563638217,\"y\":12.07},{\"x\":1685563938231,\"y\":11.8},{\"x\":1685564238362,\"y\":11.86},{\"x\":1685564538381,\"y\":11.83},{\"x\":1685564838277,\"y\":11.83},{\"x\":1685565138242,\"y\":11.79},{\"x\":1685565438280,\"y\":11.7},{\"x\":1685565738251,\"y\":11.53},{\"x\":1685566038259,\"y\":11.53},{\"x\":1685566338265,\"y\":11.53},{\"x\":1685566638303,\"y\":11.43},{\"x\":1685566938268,\"y\":11.43},{\"x\":1685567238323,\"y\":11.33},{\"x\":1685567538343,\"y\":11.39},{\"x\":1685567838304,\"y\":11.31},{\"x\":1685568138281,\"y\":11.39},{\"x\":1685568438336,\"y\":11.31},{\"x\":1685568738284,\"y\":11.31},{\"x\":1685569038329,\"y\":11.31},{\"x\":1685569338310,\"y\":11.25},{\"x\":1685569638302,\"y\":11.25},{\"x\":1685569938300,\"y\":11.25},{\"x\":1685570238298,\"y\":10.97},{\"x\":1685570538297,\"y\":10.97},{\"x\":1685570838294,\"y\":10.97},{\"x\":1685571138323,\"y\":10.78},{\"x\":1685571438384,\"y\":10.78},{\"x\":1685571738313,\"y\":10.78},{\"x\":1685572038419,\"y\":10.8},{\"x\":1685572338315,\"y\":10.8},{\"x\":1685572638324,\"y\":10.8},{\"x\":1685572938327,\"y\":10.44},{\"x\":1685573238329,\"y\":10.44},{\"x\":1685573538333,\"y\":10.44},{\"x\":1685573838369,\"y\":10.44},{\"x\":1685574138332,\"y\":10.44},{\"x\":1685574438328,\"y\":10.43},{\"x\":1685574738335,\"y\":10.43},{\"x\":1685575038372,\"y\":10.42},{\"x\":1685575338334,\"y\":10.43},{\"x\":1685575638376,\"y\":10.44},{\"x\":1685575938389,\"y\":10.48},{\"x\":1685576238419,\"y\":10.39},{\"x\":1685576538344,\"y\":10.11},{\"x\":1685576838359,\"y\":10.2},{\"x\":1685577138380,\"y\":10.29},{\"x\":1685577438375,\"y\":10.29},{\"x\":1685577738413,\"y\":10.3},{\"x\":1685578038361,\"y\":10.29},{\"x\":1685578338371,\"y\":10.3},{\"x\":1685578638503,\"y\":10.18},{\"x\":1685578938376,\"y\":10.18},{\"x\":1685579238383,\"y\":10.09},{\"x\":1685579538380,\"y\":10.17},{\"x\":1685579838438,\"y\":10.13},{\"x\":1685580138376,\"y\":9.88},{\"x\":1685580438398,\"y\":9.88},{\"x\":1685580738405,\"y\":9.94},{\"x\":1685581038616,\"y\":9.94},{\"x\":1685581338395,\"y\":9.94},{\"x\":1685581638435,\"y\":9.94},{\"x\":1685581938409,\"y\":9.88},{\"x\":1685582238480,\"y\":9.86},{\"x\":1685582538402,\"y\":9.86},{\"x\":1685582838405,\"y\":9.86},{\"x\":1685583138415,\"y\":9.86},{\"x\":1685583438613,\"y\":9.86},{\"x\":1685583738415,\"y\":9.88},{\"x\":1685584038410,\"y\":9.88},{\"x\":1685584338448,\"y\":9.79},{\"x\":1685584638454,\"y\":9.88},{\"x\":1685584938414,\"y\":9.88},{\"x\":1685585238420,\"y\":9.98},{\"x\":1685585538423,\"y\":9.89},{\"x\":1685585838440,\"y\":9.89},{\"x\":1685586138428,\"y\":9.84},{\"x\":1685586438423,\"y\":9.84},{\"x\":1685586738424,\"y\":9.79},{\"x\":1685587038648,\"y\":9.93},{\"x\":1685587338431,\"y\":9.79},{\"x\":1685587638450,\"y\":9.79},{\"x\":1685587938475,\"y\":9.88},{\"x\":1685588238559,\"y\":9.88},{\"x\":1685588538441,\"y\":9.88},{\"x\":1685588838584,\"y\":9.67},{\"x\":1685589138428,\"y\":9.58},{\"x\":1685589438536,\"y\":9.67},{\"x\":1685589738473,\"y\":9.67},{\"x\":1685590038433,\"y\":9.67},{\"x\":1685590338478,\"y\":9.67},{\"x\":1685590638761,\"y\":9.42},{\"x\":1685590938425,\"y\":9.42},{\"x\":1685591238442,\"y\":9.42},{\"x\":1685591538439,\"y\":9.31},{\"x\":1685591838504,\"y\":9.31},{\"x\":1685592138436,\"y\":9.34},{\"x\":1685592438435,\"y\":9.34},{\"x\":1685592738487,\"y\":9.38},{\"x\":1685593038723,\"y\":9.38},{\"x\":1685593338444,\"y\":9.38},{\"x\":1685593638424,\"y\":9.34},{\"x\":1685593938457,\"y\":9.34},{\"x\":1685594238655,\"y\":9.86},{\"x\":1685594538452,\"y\":9.86},{\"x\":1685594838449,\"y\":9.86},{\"x\":1685595138489,\"y\":9.84},{\"x\":1685595438547,\"y\":9.84},{\"x\":1685595738460,\"y\":9.78},{\"x\":1685596038492,\"y\":9.85},{\"x\":1685596338571,\"y\":9.85},{\"x\":1685596638545,\"y\":9.85},{\"x\":1685596938457,\"y\":9.76},{\"x\":1685597238477,\"y\":9.85},{\"x\":1685597538472,\"y\":9.8},{\"x\":1685597838494,\"y\":9.83},{\"x\":1685598138501,\"y\":9.86},{\"x\":1685598438509,\"y\":10.14},{\"x\":1685598738535,\"y\":10.14},{\"x\":1685599038819,\"y\":10.14},{\"x\":1685599338484,\"y\":10.07},{\"x\":1685599638529,\"y\":10.07},{\"x\":1685599938480,\"y\":10.14},{\"x\":1685600238698,\"y\":10.23},{\"x\":1685600538482,\"y\":10.1},{\"x\":1685600838527,\"y\":10.43},{\"x\":1685601138487,\"y\":10.43},{\"x\":1685601438740,\"y\":10.43},{\"x\":1685601738547,\"y\":10.45},{\"x\":1685602038498,\"y\":10.36},{\"x\":1685602338488,\"y\":10.43},{\"x\":1685602638682,\"y\":10.46},{\"x\":1685602938581,\"y\":10.46},{\"x\":1685603238508,\"y\":10.47},{\"x\":1685603538503,\"y\":10.47},{\"x\":1685603838572,\"y\":10.47},{\"x\":1685604138511,\"y\":10.47},{\"x\":1685604438579,\"y\":10.6},{\"x\":1685604738515,\"y\":10.51},{\"x\":1685605038560,\"y\":10.55},{\"x\":1685605338556,\"y\":11.07},{\"x\":1685605638563,\"y\":11.07},{\"x\":1685605938519,\"y\":11.07},{\"x\":1685606238594,\"y\":11.05},{\"x\":1685606538513,\"y\":11.12},{\"x\":1685606838518,\"y\":11.15},{\"x\":1685607138518,\"y\":11.15},{\"x\":1685607438879,\"y\":11.19},{\"x\":1685607738543,\"y\":11.19},{\"x\":1685608038530,\"y\":11.42},{\"x\":1685608338527,\"y\":11.33},{\"x\":1685608638628,\"y\":11.33},{\"x\":1685608938572,\"y\":11.54},{\"x\":1685609238526,\"y\":11.54},{\"x\":1685609538568,\"y\":11.54},{\"x\":1685609838674,\"y\":11.73},{\"x\":1685610138530,\"y\":11.54},{\"x\":1685610438525,\"y\":11.73},{\"x\":1685610738604,\"y\":11.81},{\"x\":1685611038666,\"y\":11.81},{\"x\":1685611338564,\"y\":11.72},{\"x\":1685611638584,\"y\":12.09},{\"x\":1685611938537,\"y\":12.09},{\"x\":1685612238596,\"y\":12.09},{\"x\":1685612538547,\"y\":12.11},{\"x\":1685612838540,\"y\":12.11},{\"x\":1685613138539,\"y\":12.07},{\"x\":1685613438767,\"y\":12.53},{\"x\":1685613738611,\"y\":12.64},{\"x\":1685614038551,\"y\":12.51},{\"x\":1685614338551,\"y\":12.64},{\"x\":1685614638741,\"y\":12.64},{\"x\":1685614938559,\"y\":12.64},{\"x\":1685615238587,\"y\":13.05},{\"x\":1685615538593,\"y\":13.15},{\"x\":1685615838836,\"y\":13.07},{\"x\":1685616138551,\"y\":13.07},{\"x\":1685616438549,\"y\":13.17},{\"x\":1685616738558,\"y\":13.26},{\"x\":1685617038760,\"y\":13.26},{\"x\":1685617338558,\"y\":13.36},{\"x\":1685617638534,\"y\":13.36},{\"x\":1685617938635,\"y\":13.73},{\"x\":1685618238566,\"y\":13.71},{\"x\":1685618538558,\"y\":13.71},{\"x\":1685618838593,\"y\":13.75},{\"x\":1685619138565,\"y\":13.75},{\"x\":1685619438673,\"y\":13.93},{\"x\":1685619738585,\"y\":13.93}","],\"labels\":",[1107,29084,26816],{},"\",\"payloadType\":\"json\",\"x\":150,\"y\":180,\"wires\":[[\"3eb08d4843164efc\"]]},{\"id\":\"41e847ff22249c0e\",\"type\":\"ui_group\",\"name\":\"Temperature\",\"tab\":\"1d985094b1a81b0c\",\"order\":5,\"disp\":true,\"width\":\"24\",\"collapse\":false,\"className\":\"\"},{\"id\":\"1d985094b1a81b0c\",\"type\":\"ui_tab\",\"name\":\"Wide View\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}",[399,29087,29089],{"id":29088},"_3-using-sliders-and-persisting-the-current-value","3. Using sliders and persisting the current value",[15,29091,29092],{},"Sliders are a really useful user-interface element. Where you need to control the speed of a piece of machinery, having the ability to use a slider rather than manually typing in a value is a much better fit for shop-floor HMIs.",[15,29094,29095],{},"When using sliders in your dashboards, it's important to consider how you will persist the state of the slider. If you don't persist the state, you will find that a redeploy of your dashboard will set the slider back to the default value. That would also change the speed of your machine.",[15,29097,29098],{},[165,29099],{"alt":29100,"src":29101,"title":29100},"An example of a slider in a HMI","\u002Fblog\u002F2023\u002F06\u002Fimages\u002Fslider-ui.gif",[15,29103,29104],{},"To retain the current value of the slider we can use Node-RED's context. Each time the slider value is updated, we store the value in context. Each time we deploy the flow, we can now load the value back from context.",[15,29106,29107],{},"If you'd like to view this slider and the flow which makes it work on your own Node-RED, you can import the flow below.",[15,29109,1105,29110,1150],{},[1107,29111,29112,29113,29115,29116,29118,29119,29122,29123,29126,29127,29129,29130,29132],{},"{\"id\":\"05340e7a133098a6\",\"type\":\"ui_slider\",\"z\":\"0e6be5088cecccc1\",\"name\":\"\",\"label\":\"",[2526,29114],{"value":1227},"\",\"tooltip\":\"\",\"group\":\"41e847ff22249c0e\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":true,\"outs\":\"all\",\"topic\":\"topic\",\"topicType\":\"msg\",\"min\":0,\"max\":\"20\",\"step\":1,\"className\":\"\",\"x\":230,\"y\":300,\"wires\":[[\"945db61c94fc0704\",\"ad7819da2cfbcd4e\"]]},{\"id\":\"849d76d146ff8c12\",\"type\":\"inject\",\"z\":\"0e6be5088cecccc1\",\"name\":\"Inject on deploy\",\"props\":",[1107,29117],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":160,\"y\":240,\"wires\":[[\"aeedb3c60965c1af\"]]},{\"id\":\"945db61c94fc0704\",\"type\":\"change\",\"z\":\"0e6be5088cecccc1\",\"name\":\"Set global-slider-value = msg.payload\",\"rules\":",[1107,29120,29121],{},"{\"t\":\"set\",\"p\":\"slider-value\",\"pt\":\"global\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":450,\"y\":300,\"wires\":[[]]},{\"id\":\"aeedb3c60965c1af\",\"type\":\"change\",\"z\":\"0e6be5088cecccc1\",\"name\":\"Set msg.payload = global.slider-value\",\"rules\":",[1107,29124,29125],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"slider-value\",\"tot\":\"global\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":410,\"y\":240,\"wires\":[[\"05340e7a133098a6\"]]},{\"id\":\"ad7819da2cfbcd4e\",\"type\":\"ui_text\",\"z\":\"0e6be5088cecccc1\",\"group\":\"41e847ff22249c0e\",\"order\":2,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"\",\"format\":\"The current value is ",[2526,29128],{"value":1227}," meters per minute\",\"layout\":\"row-spread\",\"className\":\"\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#000000\",\"x\":350,\"y\":340,\"wires\":",[1107,29131],{},"},{\"id\":\"41e847ff22249c0e\",\"type\":\"ui_group\",\"name\":\"Meters per Minute\",\"tab\":\"466e33abb95e4dd4\",\"order\":5,\"disp\":true,\"width\":\"10\",\"collapse\":false,\"className\":\"\"},{\"id\":\"466e33abb95e4dd4\",\"type\":\"ui_tab\",\"name\":\"Sliders\",\"icon\":\"dashboard\",\"order\":3,\"disabled\":false,\"hidden\":false}",[15,29134,29135,29136,29139],{},"We hope you found these tips useful, if you'd like to suggest some of your own tips which you think we should share in our future blog posts please ",[63,29137,12885],{"href":29138},"mailto:contact@flowfuse.com",". You can also read some of our previous Node-RED tips using the links below.",[15,29141,29142,29146,29148,29152,29154,29158,29160,29164,29166,29170,29172],{},[63,29143,29145],{"href":29144},"\u002Fblog\u002F2023\u002F04\u002F3-quick-node-red-tips-6\u002F","Node-RED Tips - Subflows, Link Nodes, and the Range Node",[5368,29147],{},[63,29149,29151],{"href":29150},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-5\u002F","Node-RED Tips - Importing, Exporting, and Grouping Flows",[5368,29153],{},[63,29155,29157],{"href":29156},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-4\u002F","Node-RED Tips - Smooth, Catch, and Maths",[5368,29159],{},[63,29161,29163],{"href":29162},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-3\u002F","Node-RED Tips - Exec, Filter, and Debug",[5368,29165],{},[63,29167,29169],{"href":29168},"\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-2\u002F","Node-RED Tips - Deploying, Debugging, and Delaying",[5368,29171],{},[63,29173,29175],{"href":29174},"\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-1\u002F","Node-RED Tips - Wiring Shortcuts",{"title":103,"searchDepth":104,"depth":104,"links":29177},[29178,29179,29180],{"id":28954,"depth":294,"text":28955},{"id":29046,"depth":294,"text":29047},{"id":29088,"depth":294,"text":29089},"2023-06-01","Learn three practical tips for improving your Node-RED Dashboard workflow, such as creating responsive layouts, adding multiple data series to charts, and persisting slider values.","\u002Fimages\u002Fblog\u002Fnr-quicktips.jpg",{"excerpt":29185},{"type":12,"value":29186},[29187],[15,29188,28944],{},"\u002Fblog\u002F2023\u002F06\u002F3-quick-node-red-tips-7",{"title":28938,"description":29182},{"loc":29189},"blog\u002F2023\u002F06\u002F3-quick-node-red-tips-7","Save yourself time when working with Node-RED Dashboards with these three tips.",[8650,7457,138,12631,11298],"h5GmOwlW7UuahwdTPwHCe-u7SZQDJw8zjkVdPQFXuZ4",{"id":29197,"title":29198,"authors":29199,"body":29201,"cta":3,"date":29274,"description":29275,"extension":116,"image":29276,"lastUpdated":3,"meta":29277,"navigation":129,"path":29288,"seo":29289,"sitemap":29290,"stem":29291,"subtitle":29292,"tags":29293,"tldr":3,"video":3,"__hash__":29294},"blog\u002Fblog\u002F2023\u002F05\u002Fnode-red-community-survey-results.md","Node-RED Community Survey Results",[29200],"ian-skerrett",{"type":12,"value":29202,"toc":29272},[29203,29216,29265],[15,29204,29205,29206,29210,29211,29215],{},"The Node-RED community recently published the results of their ",[63,29207,29209],{"href":596,"rel":29208},[67],"2023 Community Survey",", building upon their ",[63,29212,29214],{"href":587,"rel":29213},[67],"2019 survey",". The findings reveal some interesting trends within the Node-RED community that are worth highlighting.",[271,29217,29218,29227,29242,29256],{},[42,29219,29220,29223,29224,29226],{},[19,29221,29222],{},"Passionate and Experienced Community",". The Node-RED community has shown a remarkable increase in experience. In 2019, only 28.3% of users had been utilizing Node-RED for over two years. However, in 2023, this number has grown to an impressive 65.2%. This indicates that Node-RED has become a go-to tool for many individuals, demonstrating their continued loyalty to the platform. Moreover, the community highly regards Node-RED, with 94% of respondents rating it as a 4 or 5 on a scale of 1 to 5.",[5368,29225],{},"Developer tools can fall out of fashion but it is clear Node-RED is providing value and is being used by developers.",[42,29228,29229,29232,29233,1413,29237,29241],{},[19,29230,29231],{},"Increasing Adoption in Industrial and Manufacturing Automation",". The survey also revealed a significant increase in Node-RED usage within the manufacturing and industrial automation industries. Several data points support this finding, including a rise from 31.5% to 40.3% in respondents identifying themselves as working in the manufacturing industry. Additionally, there has been an increase in the use of Node-RED applications for Industrial IoT\u002FPLC devices, which grew from 24% to 35.8% in 2023. Furthermore, the adoption of popular manufacturing industry protocols like ",[63,29234,2641],{"href":29235,"rel":29236},"https:\u002F\u002Fopcfoundation.org\u002F",[67],[63,29238,2646],{"href":29239,"rel":29240},"https:\u002F\u002Fmodbus.org\u002F",[67]," has also increased, with OPC-UA usage rising from 9.3% to 16.7% and Modbus usage increasing from 15.8% to 27.6%.",[42,29243,29244,29,29247,29252,29253,29255],{},[19,29245,29246],{},"InfluxDB dominance in the Node-RED community",[63,29248,29251],{"href":29249,"rel":29250},"https:\u002F\u002Fwww.influxdata.com\u002F",[67]," InfluxDB"," has emerged as the leading database within the Node-RED community. Its usage has grown significantly, from 24.2% in 2019 to 43.9% in 2023. On the other hand, MySQL, the next most popular database, experienced a slight decrease in usage, dropping from 32.4% to 31.4%.",[5368,29254],{},"It is evident that the MING stack (MQTT\u002FMosquitto, InfluxDB, Node-RED, and Grafana) is gaining momentum and becoming a preferred choice for developers.",[42,29257,29258,29261,29262,29264],{},[19,29259,29260],{},"Limitations to Node-RED Adoption",". This year's survey also explored factors that might limit the adoption of Node-RED. Approximately 27.9% of respondents stated that they perceived no additional need for Node-RED. However, the next most common responses were related to the perception that Node-RED is only suitable for proof-of-concept (POC) projects (19.9%), the lack of specific Node-RED features (13.3%), and the absence of professional support (10.1%).",[5368,29263],{},"It is important for the Node-RED community to demonstrate the platform's usage in production environments. Although changing the perception of Node-RED as solely a POC tool may take time, FlowFuse is committed to helping shift this perspective. Moreover, FlowFuse aims to address other identified issues by developing the FlowFuse platform, which will provide the necessary features to create reliable, secure, and dependable Node-RED applications. FlowFuse will also offer professional support to all its customers, ensuring that users have the assistance they need.",[15,29266,29267,29268,29],{},"Thank you to everyone that completed the Node-RED Survey. The insights from the survey will help the community to build a better Node-RED. Check out the detailed results on the ",[63,29269,29271],{"href":596,"rel":29270},[67],"nodered.org website",{"title":103,"searchDepth":104,"depth":104,"links":29273},[],"2023-05-31","The Node-RED community recently published the results of their 2023 Community Survey. A number of interesting trends were identified from the survey results.","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fnr-community-survey.jpg",{"excerpt":29278},{"type":12,"value":29279},[29280],[15,29281,29205,29282,29210,29285,29215],{},[63,29283,29209],{"href":596,"rel":29284},[67],[63,29286,29214],{"href":587,"rel":29287},[67],"\u002Fblog\u002F2023\u002F05\u002Fnode-red-community-survey-results",{"title":29198,"description":29275},{"loc":29288},"blog\u002F2023\u002F05\u002Fnode-red-community-survey-results","Discover the trends and insights into the Node-RED community",[8650,138,137],"m2TwEkojguwLWd5J7-59JYne5sxkYRy-ZWl6U0xxv44",{"id":29296,"title":29297,"authors":29298,"body":29299,"cta":3,"date":29604,"description":29605,"extension":116,"image":29606,"lastUpdated":3,"meta":29607,"navigation":129,"path":29615,"seo":29616,"sitemap":29617,"stem":29618,"subtitle":29619,"tags":29620,"tldr":3,"video":3,"__hash__":29621},"blog\u002Fblog\u002F2023\u002F05\u002Fpersisting-chart-data-in-node-red.md","Persisting chart data in Node-RED Dashboard 1",[19494],{"type":12,"value":29300,"toc":29594},[29301,29307,29310,29333,29337,29340,29343,29347,29350,29353,29368,29372,29375,29378,29382,29385,29388,29404,29407,29410,29428,29431,29437,29440,29444,29447,29450,29470,29473,29497,29500,29504,29507,29532,29535,29541,29548,29551,29557,29586,29589,29591],[15,29302,29303,29304,29],{},"Node-RED makes it easy to create HMI (Human Machine Interfaces) using ",[63,29305,11862],{"href":11860,"rel":29306},[67],[15,29308,29309],{},"One of the most useful features of Dashboard 1 is the ability to store historic data passed to a chart within the chart node itself. This makes your flows far simpler than would be the case if you needed to send the entire data set to the chart for each update.",[17286,29311,29314,29315,29314,29326],{"className":29312},[29313],"blog-update-notes","\n    ",[15,29316,29317,29320,29321,29],{},[19,29318,29319],{},"UPDATE:"," Since this article was published, Node-RED Dashboard (1.0) has been ",[63,29322,29325],{"href":29323,"target":29324},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fannouncement-node-red-dashboard-v1-deprecation-notice\u002F89006","_blank","deprecated",[15,29327,29328,29329,29332],{},"Instead, it is recommended to use ",[63,29330,29331],{"href":3463},"FlowFuse Dashboard (Dashboard 2.0)"," which is a more modern and feature-rich dashboard solution for Node-RED.",[399,29334,29336],{"id":29335},"the-importance-of-persisting-chart-data","The Importance of Persisting Chart Data",[15,29338,29339],{},"Storing the data in the chart node is fine to show prototypes of HMIs, but where it's vital the correct data is always shown we are going to need a backup. Data can easily be lost when you move your flow to a new device, restart your instance, or simply when upgrading Node-RED.",[15,29341,29342],{},"How can we store our chart data so we can be confident it will be there each time a user views your HMI?",[399,29344,29346],{"id":29345},"example-dashboard","Example Dashboard",[15,29348,29349],{},"In this example, we are passing in a random number between one and 10 each second. With each new value received the chart updates and as mentioned about, the values are also stored in the chart node.",[15,29351,29352],{},"If you'd like to see and edit the flows I've created, you can copy and paste the JSON below into your Node-RED import feature.",[15,29354,4917,29355,29367],{},[1107,29356,29357,29358,29360,29361,29363,29364,29366],{},"{\"id\":\"c6825b1001216b89\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"props\":",[1107,29359],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":110,\"y\":220,\"wires\":[[\"6b609d978540fb2a\"]]},{\"id\":\"6b609d978540fb2a\",\"type\":\"Number\",\"z\":\"668c56888fd0f960\",\"name\":\"Random Number\",\"minimum\":\"1\",\"maximum\":\"10\",\"roundTo\":\"0\",\"Floor\":true,\"x\":270,\"y\":220,\"wires\":[[\"794846db6dc8cef8\"]]},{\"id\":\"794846db6dc8cef8\",\"type\":\"ui_chart\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"group\":\"af1535b39b74f94a\",\"order\":0,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29362,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":430,\"y\":220,\"wires\":[[\"ad53848ee4b0d91e\"]]},{\"id\":\"ad53848ee4b0d91e\",\"type\":\"debug\",\"z\":\"668c56888fd0f960\",\"name\":\"debug\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":550,\"y\":220,\"wires\":",[1107,29365],{},"},{\"id\":\"af1535b39b74f94a\",\"type\":\"ui_group\",\"name\":\"Example\",\"tab\":\"14f1442eb7525190\",\"order\":1,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"14f1442eb7525190\",\"type\":\"ui_tab\",\"name\":\"Home\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}","\n{% endrenderFlow %}\nx",[399,29369,29371],{"id":29370},"how-can-we-store-and-recall-the-chart-data","How can we store and recall the chart data?",[15,29373,29374],{},"The chart node has a really useful feature which allows us to access all the data currently shown in the chart. Each time the chart receives new data, it's added to the existing values then the whole data set is sent out the outbound port of the chart node.",[15,29376,29377],{},"Now that we have a way to easily access the chart data in a single payload, we next need to store that data somewhere safer. I'm going to explain 3 potential solutions, which I use on a regular basis.",[1165,29379,29381],{"id":29380},"_1-node-red-file-out-and-file-in-nodes","1. Node-RED file-out and file-in nodes",[15,29383,29384],{},"Node-RED can read and write data to a local filesystem. Being that we already have the chart data in a single payload, we just need to write that payload to a file for later use, which we can do using the file-out node.",[15,29386,29387],{},"This example flow shows how to use the file-out node to write the chart data to your local filesystem.",[15,29389,4917,29390,1150],{},[1107,29391,29392,29393,29395,29396,29398,29399,29401,29402,29366],{},"{\"id\":\"ead9df683d29fb8a\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"props\":",[1107,29394],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":110,\"y\":560,\"wires\":[[\"ef5359b8bd3f78b3\"]]},{\"id\":\"ef5359b8bd3f78b3\",\"type\":\"Number\",\"z\":\"668c56888fd0f960\",\"name\":\"Random Number\",\"minimum\":\"1\",\"maximum\":\"10\",\"roundTo\":\"0\",\"Floor\":true,\"x\":270,\"y\":560,\"wires\":[[\"69ad440cd8d1ce30\"]]},{\"id\":\"69ad440cd8d1ce30\",\"type\":\"ui_chart\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"group\":\"af1535b39b74f94a\",\"order\":0,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29397,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":430,\"y\":560,\"wires\":[[\"e4e7758028477505\",\"d9d6a2e34767f568\"]]},{\"id\":\"e4e7758028477505\",\"type\":\"debug\",\"z\":\"668c56888fd0f960\",\"name\":\"debug\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":550,\"y\":560,\"wires\":",[1107,29400],{},"},{\"id\":\"d9d6a2e34767f568\",\"type\":\"json\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"property\":\"payload\",\"action\":\"\",\"pretty\":false,\"x\":550,\"y\":600,\"wires\":[[\"b5b020fb17f615df\"]]},{\"id\":\"b5b020fb17f615df\",\"type\":\"file\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"filename\":\"example.json\",\"filenameType\":\"str\",\"appendNewline\":true,\"createDir\":false,\"overwriteFile\":\"true\",\"encoding\":\"none\",\"x\":690,\"y\":600,\"wires\":[[\"a6d9eab41d4dcf97\"]]},{\"id\":\"a6d9eab41d4dcf97\",\"type\":\"debug\",\"z\":\"668c56888fd0f960\",\"name\":\"debug 92\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":840,\"y\":600,\"wires\":",[1107,29403],{},[15,29405,29406],{},"As the chart node sends the full data set each time new data is added, we overwrite the content of the file rather than append the new values.",[15,29408,29409],{},"The next step is to pull the data back from the filesystem to your Node-RED instance. Node-RED makes this very easy using the file-in node.",[15,29411,4917,29412,1150],{},[1107,29413,29392,29414,29395,29416,29398,29418,29401,29420,29422,29423,29425,29426,29366],{},[1107,29415],{},[1107,29417,24639],{},[1107,29419],{},[1107,29421],{},"},{\"id\":\"e9cb9350f1aaeb38\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"import data\",\"props\":",[1107,29424],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":110,\"y\":660,\"wires\":[[\"600f014947f73d8f\"]]},{\"id\":\"600f014947f73d8f\",\"type\":\"file in\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"filename\":\"example.json\",\"filenameType\":\"str\",\"format\":\"utf8\",\"chunk\":false,\"sendError\":false,\"encoding\":\"none\",\"allProps\":false,\"x\":270,\"y\":660,\"wires\":[[\"972118c0e114f47b\",\"69ad440cd8d1ce30\"]]},{\"id\":\"972118c0e114f47b\",\"type\":\"debug\",\"z\":\"668c56888fd0f960\",\"name\":\"debug 93\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":420,\"y\":660,\"wires\":",[1107,29427],{},[15,29429,29430],{},"When you press the 'import data' trigger node, the data is loaded in from the filesystem and shown in the chart. You may want to automate that task to run each you deploy your Node-RED instance.",[15,29432,29433],{},[165,29434],{"alt":29435,"src":29436,"title":29435},"Import data on deploy","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Finject-on-deploy.png",[15,29438,29439],{},"Bear in mind that your data is stored in your filesystem, if your storage drive fails you will lose your data, you might want to consider taking backups and storing elsewhere for emergencies.",[1165,29441,29443],{"id":29442},"_2-flowfuses-persistent-context","2. FlowFuse's persistent context",[15,29445,29446],{},"FlowFuse Cloud and premium self hosted version provides persistent context storage as part of its Node-RED instances. This allows you to create, read, update, and delete data as needed, even if you have restarted a Node-RED instance.",[15,29448,29449],{},"This flow shows chart data being sent to persistent context so we can access it later. The process is very similar to using the file-out and file-in nodes.",[15,29451,4917,29452,1150],{},[1107,29453,29454,29455,29457,29458,29460,29461,29463,29464,29467,29468,29366],{},"{\"id\":\"c6825b1001216b89\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":",[1107,29456],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":170,\"y\":100,\"wires\":[[\"6b609d978540fb2a\"]]},{\"id\":\"794846db6dc8cef8\",\"type\":\"ui_chart\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"group\":\"af1535b39b74f94a\",\"order\":0,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29459,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":490,\"y\":100,\"wires\":[[\"ad53848ee4b0d91e\",\"938c7d878545e623\"]]},{\"id\":\"ad53848ee4b0d91e\",\"type\":\"debug\",\"z\":\"4767c2f7095bee53\",\"name\":\"debug\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":610,\"y\":100,\"wires\":",[1107,29462],{},"},{\"id\":\"6b609d978540fb2a\",\"type\":\"Number\",\"z\":\"4767c2f7095bee53\",\"name\":\"Random Number\",\"minimum\":\"1\",\"maximum\":\"10\",\"roundTo\":\"0\",\"Floor\":true,\"x\":330,\"y\":100,\"wires\":[[\"794846db6dc8cef8\"]]},{\"id\":\"938c7d878545e623\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"rules\":",[1107,29465,29466],{},"{\"t\":\"set\",\"p\":\"#:(persistent)::chart-data\",\"pt\":\"global\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":660,\"y\":140,\"wires\":[[\"3792cc96a748e75a\"]]},{\"id\":\"3792cc96a748e75a\",\"type\":\"debug\",\"z\":\"4767c2f7095bee53\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":840,\"y\":140,\"wires\":",[1107,29469],{},[15,29471,29472],{},"We now need to have a method to load the data back into our chart. We will again use a manual 'import data' trigger to load the full set of data from the persistent context, and then push it back into the chart.",[15,29474,4917,29475,1150],{},[1107,29476,29454,29477,29457,29479,29460,29481,29463,29483,29467,29485,29487,29488,29490,29491,29494,29495,29366],{},[1107,29478],{},[1107,29480,24639],{},[1107,29482],{},[1107,29484,29466],{},[1107,29486],{},"},{\"id\":\"3379276c77b4691c\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"import data\",\"props\":",[1107,29489],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":180,\"wires\":[[\"ddd1ef41321fb4a6\"]]},{\"id\":\"ddd1ef41321fb4a6\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"rules\":",[1107,29492,29493],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"#:(persistent)::chart-data\",\"tot\":\"global\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":320,\"y\":180,\"wires\":[[\"794846db6dc8cef8\",\"fa65b958e34bc971\"]]},{\"id\":\"fa65b958e34bc971\",\"type\":\"debug\",\"z\":\"4767c2f7095bee53\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":480,\"y\":180,\"wires\":",[1107,29496],{},[15,29498,29499],{},"You may have noticed that when we are pushing duplicate data into the chart it automatically checks to see if the data is already stored. If the data points are already in the chart the new data is disregard. This saves us writing an extra section of the flow to delete the data before we load it in.",[1165,29501,29503],{"id":29502},"_3-expose-the-data-via-an-api-then-manually-import-it","3. Expose the data via an API then manually import it",[15,29505,29506],{},"In some cases you may want to copy your chart data to somewhere outside of your Node-RED instances. You can do this by creating a simple API which allows an outside system to request the chart data. You can also manually go to the URL of the API in a web browser to get your data. This example flow allows a user or system to access the chart data via a URL.",[15,29508,4917,29509,1150],{},[1107,29510,29511,29512,29514,29515,29517,29518,29520,29521,29524,29525,29527,29528,29531],{},"{\"id\":\"6523e86042252710\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":",[1107,29513],{},",\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":540,\"wires\":[[\"d51aba5f9a808592\"]]},{\"id\":\"427c60f2c4f523b7\",\"type\":\"ui_chart\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"group\":\"af1535b39b74f94a\",\"order\":0,\"width\":0,\"height\":0,\"label\":\"chart\",\"chartType\":\"line\",\"legend\":\"false\",\"xformat\":\"HH:mm:ss\",\"interpolate\":\"linear\",\"nodata\":\"\",\"dot\":false,\"ymin\":\"\",\"ymax\":\"\",\"removeOlder\":1,\"removeOlderPoints\":\"\",\"removeOlderUnit\":\"3600\",\"cutout\":0,\"useOneColor\":false,\"useUTC\":false,\"colors\":",[1107,29516,24639],{},",\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":470,\"y\":540,\"wires\":[[\"ba84cd1820120139\",\"d9087436f4769691\"]]},{\"id\":\"ba84cd1820120139\",\"type\":\"debug\",\"z\":\"4767c2f7095bee53\",\"name\":\"debug\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":590,\"y\":540,\"wires\":",[1107,29519],{},"},{\"id\":\"d51aba5f9a808592\",\"type\":\"Number\",\"z\":\"4767c2f7095bee53\",\"name\":\"Random Number\",\"minimum\":\"1\",\"maximum\":\"10\",\"roundTo\":\"0\",\"Floor\":true,\"x\":310,\"y\":540,\"wires\":[[\"427c60f2c4f523b7\"]]},{\"id\":\"d9087436f4769691\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"rules\":",[1107,29522,29523],{},"{\"t\":\"set\",\"p\":\"chart-data\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":630,\"y\":580,\"wires\":[[]]},{\"id\":\"4dd97b29430ad2ba\",\"type\":\"http in\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"url\":\"\u002Fdata\",\"method\":\"get\",\"upload\":false,\"swaggerDoc\":\"\",\"x\":200,\"y\":640,\"wires\":[[\"b4f0eb085cc91834\"]]},{\"id\":\"7ca49e5465699355\",\"type\":\"http response\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"statusCode\":\"\",\"headers\":{},\"x\":670,\"y\":640,\"wires\":",[1107,29526],{},"},{\"id\":\"b4f0eb085cc91834\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"Get the chart data from flow.chart-data\",\"rules\":",[1107,29529,29530],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"chart-data\",\"tot\":\"flow\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":440,\"y\":640,\"wires\":[[\"7ca49e5465699355\"]]},{\"id\":\"af1535b39b74f94a\",\"type\":\"ui_group\",\"name\":\"Example\",\"tab\":\"14f1442eb7525190\",\"order\":1,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"14f1442eb7525190\",\"type\":\"ui_tab\",\"name\":\"Home\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}",[15,29533,29534],{},"We can now access the data by simply visiting the URL of the API.",[15,29536,29537],{},[165,29538],{"alt":29539,"src":29540,"title":29539},"The chart data accessed via the API in a web browser","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fdata-in-browser.png",[15,29542,29543,29544,29],{},"Bear in mind that you should secure the API as appropriate for the data. If you don't put security around the API, anyone on the same network as your Node-RED instance can access your chart data. Potentially that could give access to our data to the whole internet, so where needed take steps to keep your data safe. You can read more about securing Node-RED in our ",[63,29545,29547],{"href":29546},"\u002Fblog\u002F2023\u002F04\u002Fsecuring-node-red-in-production\u002F","blog post here",[15,29549,29550],{},"We now need to get that data back into a Node-RED instance. We can do that by editing a node and pasting in the data we got from the API. The flow below shows where you can paste in your data, you will then need to deploy and manually trigger 'import data'.",[15,29552,29553],{},[165,29554],{"alt":29555,"src":29556,"title":29555},"Inject the data in JSON format","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Finject-the-json.png",[15,29558,4917,29559,1150],{},[1107,29560,29511,29561,29514,29563,29517,29565,29520,29567,29569,29570,29527,29572,29574,29575,29577,29578,29585],{},[1107,29562],{},[1107,29564,24639],{},[1107,29566],{},[1107,29568,29523],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":630,\"y\":580,\"wires\":[[]]},{\"id\":\"4dd97b29430ad2ba\",\"type\":\"http in\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"url\":\"\u002Fdata\",\"method\":\"get\",\"upload\":false,\"swaggerDoc\":\"\",\"x\":200,\"y\":660,\"wires\":[[\"b4f0eb085cc91834\"]]},{\"id\":\"7ca49e5465699355\",\"type\":\"http response\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"statusCode\":\"\",\"headers\":{},\"x\":670,\"y\":660,\"wires\":",[1107,29571],{},[1107,29573,29530],{},",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":440,\"y\":660,\"wires\":[[\"7ca49e5465699355\"]]},{\"id\":\"d13d16b33ec638b2\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"import data\",\"props\":",[1107,29576,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"",[1107,29579,26813,29580,29582,29583,4957],{},[1107,29581,26816],{},",\"data\":[[{\"x\":1684841975036,\"y\":8},{\"x\":1684841976037,\"y\":6},{\"x\":1684841977038,\"y\":7},{\"x\":1684841978037,\"y\":7}]],\"labels\":",[1107,29584,26816],{},"\",\"payloadType\":\"json\",\"x\":170,\"y\":600,\"wires\":[[\"427c60f2c4f523b7\"]]},{\"id\":\"af1535b39b74f94a\",\"type\":\"ui_group\",\"name\":\"Example\",\"tab\":\"14f1442eb7525190\",\"order\":1,\"disp\":true,\"width\":\"6\",\"collapse\":false,\"className\":\"\"},{\"id\":\"14f1442eb7525190\",\"type\":\"ui_tab\",\"name\":\"Home\",\"icon\":\"dashboard\",\"disabled\":false,\"hidden\":false}",[15,29587,29588],{},"Each of these solutions has strengths and weaknesses but there are many other ways to persist your chart data. You should consider which approach is the best fit for your needs. To be as confident as possible that you data is safe, you may decide to push your data to dedicate external storage such as a database or backup solution.",[399,29590,1425],{"id":1424},[15,29592,29593],{},"Node-RED Dashboard 1 allows you to easily make informative HMIs, but it's important to make sure the chart data you are showing is stored safely. The approaches we have discussed above should give you a good start in ensuring your charts are populated with the correct data, even if your Node-RED instance crashes or you need to move it to a new hosting location.",{"title":103,"searchDepth":104,"depth":104,"links":29595},[29596,29597,29598,29603],{"id":29335,"depth":294,"text":29336},{"id":29345,"depth":294,"text":29346},{"id":29370,"depth":294,"text":29371,"children":29599},[29600,29601,29602],{"id":29380,"depth":104,"text":29381},{"id":29442,"depth":104,"text":29443},{"id":29502,"depth":104,"text":29503},{"id":1424,"depth":294,"text":1425},"2023-05-25","Chart data in Node-RED can be stored directly in your flows but it's a good idea to also store data eleswhere. In this article we are looking at some easy ways to persist your historic chart data in Dashboard 1","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fpersisting-data-header.jpeg",{"excerpt":29608},{"type":12,"value":29609},[29610],[15,29611,29303,29612,29],{},[63,29613,11862],{"href":11860,"rel":29614},[67],"\u002Fblog\u002F2023\u002F05\u002Fpersisting-chart-data-in-node-red",{"title":29297,"description":29605},{"loc":29615},"blog\u002F2023\u002F05\u002Fpersisting-chart-data-in-node-red","Keep your historic chart data safe and available",[8650,138,7457,12631],"XOV8jlfg8Ym1N-NeSNj2So7YX36lMt0P73DX8r4OFxA",{"id":29623,"title":29624,"authors":29625,"body":29626,"cta":29834,"date":29837,"description":29838,"extension":116,"image":29839,"lastUpdated":6203,"meta":29840,"navigation":129,"path":29846,"seo":29847,"sitemap":29848,"stem":29849,"subtitle":29850,"tags":29851,"tldr":29853,"video":3,"__hash__":29854},"blog\u002Fblog\u002F2023\u002F05\u002Fintegrating-modbus-with-node-red.md","Best Practices Integrating a Modbus Device With Node-RED (2026)",[26337],{"type":12,"value":29627,"toc":29825},[29628,29631,29634,29637,29641,29644,29650,29653,29659,29662,29668,29672,29675,29681,29685,29688,29694,29697,29703,29706,29712,29715,29719,29732,29737,29740,29746,29749,29754,29757,29763,29770,29774,29777,29780,29793,29799,29805,29811,29813,29816,29822],[15,29629,29630],{},"The world of industrial automation is slow to adopt new technology. With legacy equipment already working and in place, paralyzing down-time costs, and fears of introducing instability into a plant, technology change has a cautious pace.",[15,29632,29633],{},"Node-RED provides a way to extend the capabilities of the simpler, proven technology, allowing connections with modern systems.  However, the same fundamentals that have made industrial equipment dependable, must be incorporated into your Node-RED architecture.  And, inversely, the same abstraction and simplicity that makes a low-code, Node-RED environment fast, and easy to work with, can also make it difficult to interface with a lower-level system.",[15,29635,29636],{},"Modbus is a widely adopted protocol for accessing data from existing legacy manufacturing equipment. Node-RED makes it very easy to connect to Modbus enabled equipment. However, there are some best practices we have developed to maintain system integrity when integrating Modbus devices with Node-RED:",[399,29638,29640],{"id":29639},"add-watchdogs-to-node-red-flows","Add Watchdogs to Node-RED Flows",[15,29642,29643],{},"To keep your automation system running with peace of mind and little human intervention, use a watchdog timer.  A watchdog timer is typically used to detect and recover from system malfunctions. In Node-RED a watchdog timer can be tied to a broadcast messaging system to get push alerts directly to cell phones, as well as to an auto-reset to try to get your Flows back online automatically.  A simple implementation of this is to just put two Trigger nodes in a loop, the first one to send an alert when it hasn’t seen any recent events, and the second to to reset the first one.",[15,29645,29646],{},[165,29647],{"alt":29648,"src":29649,"title":29648},"Example watchdog flow","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-3.png",[15,29651,29652],{},"Each of the trigger nodes are setup with similar parameters, only the delay values differ.",[15,29654,29655],{},[165,29656],{"alt":29657,"src":29658,"title":29657},"Trigger node configuration","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-14.png",[15,29660,29661],{},"Sending a payload of {\"connectorType\":\"TCP\"} to the Modbus Flex Connector is enough to reset the connection without needing to send all the other parameters.",[15,29663,29664],{},[165,29665],{"alt":29666,"src":29667,"title":29666},"Change node configuration","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-10.png",[399,29669,29671],{"id":29670},"choosing-a-safe-poll-rate","Choosing a Safe Poll Rate",[15,29673,29674],{},"Be careful when adding new traffic to an industrial network, which might not be able to handle the extra load.  Some networks have low-bandwidth, especially at large plants that might be using antiquated IP devices and long-distance, Wi-Fi bridges in electrically noisy environments.  Furthermore, some PLCs and other Modbus devices have limits as to how many other devices can connect to them, and a new connection might not work, or worse, bump off an old connection.  A PLC’s general operation is to poll its inputs every cycle and react accordingly, running logic and triggering outputs as quickly as possible. The general use-case for Node-RED with a PLC is to create an HMI or broader, SCADA system.  A poll rate of every second suffices for a simple HMI.  For dashboards published to a greater audience, rather than just the operator, poll rates of several minutes to hours might be adequate.  If this is a new integration, it’s better to start slow and make sure the current infrastructure can handle the extra load.",[15,29676,29677],{},[165,29678],{"alt":29679,"src":29680,"title":29679},"Modbus node configuration","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-7.png",[399,29682,29684],{"id":29683},"coilregister-grouping","Coil\u002FRegister Grouping",[15,29686,29687],{},"Modbus works more efficiently when it is reading and writing addresses as groups.  Creating banks of consecutively numbered coils or registers can help with this.  If Modbus is already in use, perhaps for an existing HMI, but for your Node-RED dashboard you just want a selection of these addresses and they are too scattered to be read all at once, pick a starting address numbered far higher than what you will use for any HMI work and create a new bank of coils or registers just for the Node-RED dashboard.  The PLC can handle this easily and it greatly simplifies the polling for your Node-RED dashboard.  On the Node-RED side of the connection, it’s a good idea to parse and to filter this data as soon as it enters the flow.  Below, the first node creates an appropriate message for each coil, with a topic name and a true\u002Ffalse payload.  This message is then filtered by the “block unless value changes” mode in the filter node and finally a switch (by topic) node separates out each message.  In this example, every Tag coming in from the PLC only triggers downstream nodes when there is a change and each Tag has its own output to connect a wire, and subsequent nodes to.",[15,29689,29690],{},[165,29691],{"alt":29692,"src":29693,"title":29692},"Using a switch node to separate the messages","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-5.png",[15,29695,29696],{},"The filter node blocks redundant data from triggering subsequent nodes.",[15,29698,29699],{},[165,29700],{"alt":29701,"src":29702,"title":29701},"Filter node configuration","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-12.png",[15,29704,29705],{},"The function node, “modbusMapArray,” creates messages that are much more user-friendly in the Node-RED environment.",[15,29707,29708],{},[165,29709],{"alt":29710,"src":29711,"title":29710},"Modbus function node","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-1.png",[15,29713,29714],{},"Quick tip: If your PLC environment has limits on the number of addresses allowed and you want to read a lot of coils, you can work with registers bitwise and stuff 16 coils into one register.",[399,29716,29718],{"id":29717},"reading-data-types","Reading Data Types",[15,29720,29721,29722,29725,29726,29731],{},"No matter what data type the PLC is sending over Modbus, it’s going to be sent using the 16-bit registers.  For example, to send 1234.5678 as a 32-bit Float (little-endian), the payload from the PLC will be a seemingly unhelpful array, ",[1107,29723,29724],{},"21035,17562",".  I can simulate this with the ",[63,29727,29730],{"href":29728,"rel":29729},"https:\u002F\u002Fwww.automationdirect.com\u002Fadc\u002Foverview\u002Fcatalog\u002Fsoftware_products\u002Fprogrammable_controller_software\u002Fproductivity_suite_programming_software",[67],"Productivity Suite Programming Software"," from Automation Direct set to simulator mode.  Below, I have created a 32-bit float “Tag,” named “mySampleFloat32,” using modbus registers 40001 and 40002, and set the “Init Value” to 1234.5678.",[15,29733,29734],{},[165,29735],{"alt":29730,"src":29736,"title":29730},"\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-11.png",[15,29738,29739],{},"Node-RED is typically used at a much higher level, but luckily there is still a way to work with this low-level data.  Node-RED uses the Buffer Class to work with this type of data stream, but it’s a little tricky.  First the 16-bit registers have to be broken into 8-bit chunks, here we use msg.responseBuffer.buffer to retrieve each octet.  Once our buffer is properly filled, there are many built-in functions to reconstruct the data into the actual number you are looking for.",[15,29741,29742],{},[165,29743],{"alt":29744,"src":29745,"title":29744},"Using msg.responseBuffer.buffer to retrieve each octet","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-4.png",[15,29747,29748],{},"Notice below that to read these 2 registers at 400001 and 400002 I have set my Modbus-Read node to start at “Address” 0 and ready “Quantity” 2 registers.  Unfortunately, there are two different standards for writing Modbus addresses and my PLC uses the traditional convention (400001 to 465536) and this Modbus node uses the hexadecimal convention (4x0000 to 4xFFFF).",[15,29750,29751],{},[165,29752],{"alt":29679,"src":29753,"title":29679},"\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-8.png",[15,29755,29756],{},"In this example the byte order from msg.responseBuffer.buffer doesn’t quite match the data type, so we have to rebuild the buffer.",[15,29758,29759],{},[165,29760],{"alt":29761,"src":29762,"title":29761},"Mapping the data in a function node","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-13.png",[15,29764,29765,29766,29],{},"Amazingly after all that work we get the response: 1234.5677490234375.  This is the shortcoming of the 32-bit float data type.  Although it can handle a huge range of values, they aren’t very accurate.  For this reason, many times PLCs will send a number as an integer, say 12345678, and the documentation will prescribe 4 decimal place accuracy to bring the number back to 1234.5678.  Find out more ways to work with a Buffer at ",[63,29767,29768],{"href":29768,"rel":29769},"https:\u002F\u002Fnodejs.org\u002Fdist\u002Flatest-v10.x\u002Fdocs\u002Fapi\u002Fbuffer.html",[67],[399,29771,29773],{"id":29772},"general-architecture","General Architecture",[15,29775,29776],{},"Although you can off-load some of the higher-level logic from the PLC into Node-RED, it’s important to remember that Node-RED augments, but doesn’t create a replacement for a PLC’s IDE and the IEC 61131-3 suite of languages.  Make a conscious distinction between the type of work the PLC should handle and what you expect from Node-RED.  Any real-time responses to inputs should strictly be handled by the PLC.",[399,29778,10371],{"id":29779},"security",[15,29781,29782,29783,29788,29789],{},"Connecting Node-RED to your PLC also creates a larger attack surface for cyber threats.  Make sure that you follow the guidelines found on the Node-RED.org site at ",[63,29784,29787],{"href":29785,"rel":29786},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Fruntime\u002Fsecuring-node-red",[67],"Securing Node-RED",".  Node-RED’s strength is its ability to make connections where they weren’t possible before, but this can be taken advantage of by a hacker.  For instance, maybe it’s tempting to make Node-RED a transparent gateway and make a RESTful API fully exposing a modbus-flex-write node.  This is amazingly easy and powerful with Node-RED, but anyone who can access your IP could send http:\u002F\u002F",[29790,29791,29792],"your-ip",{},":1880\u002Fcareful?value=true&fc=15&unitid=1&address=0&quantity=10 and remotely turn on and off whatever they wanted.",[15,29794,29795],{},[165,29796],{"alt":29797,"src":29798,"title":29797},"Example endpoint flow","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-6.png",[15,29800,29801,29802],{},"Instead, a better practice would be to more narrowly define what you want to accomplish and only allow Node-RED to do exactly that. In this case you might send http:\u002F\u002F",[29790,29803,29804],{},":1880\u002FhonkTheLunchHorn?honk=true",[15,29806,29807],{},[165,29808],{"alt":29809,"src":29810,"title":29809},"Locking down the endpoint","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-2.png",[399,29812,14042],{"id":14041},[15,29814,29815],{},"If not done right, there could be some hard lessons, so it’s best to monitor the processes to help track down bugs.  Add a log, keep your eyes out, and as a community let’s work to create stable systems.",[15,29817,29818],{},[165,29819],{"alt":29820,"src":29821,"title":29820},"Logging failures","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fintegrating-modbus-9.png",[15,29823,29824],{},"How readily upper management gives an “okay” to this new technology comes with how well it is implemented.  There will be some growing pains, but by the end, you will have supercharged your plant, bringing it into the 21st century.",{"title":103,"searchDepth":104,"depth":104,"links":29826},[29827,29828,29829,29830,29831,29832,29833],{"id":29639,"depth":294,"text":29640},{"id":29670,"depth":294,"text":29671},{"id":29683,"depth":294,"text":29684},{"id":29717,"depth":294,"text":29718},{"id":29772,"depth":294,"text":29773},{"id":29779,"depth":294,"text":10371},{"id":14041,"depth":294,"text":14042},{"type":748,"title":29835,"description":29836},"Connect Modbus Devices to the Cloud with FlowFuse","FlowFuse gives you a secure, managed platform for deploying Node-RED flows that connect your legacy Modbus equipment to modern systems, dashboards, and cloud services.","2023-05-16","Modbus is a widely adopted protocol for accessing data from existing legacy manufacturing equipment. Node-RED makes it very easy to connect to Modbus enabled equipment. However, there are some best practices we have developed to maintain system integrity when integrating Modbus devices with Node-RED","\u002Fblog\u002F2023\u002F05\u002Fimages\u002Fmodbus2.jpg",{"keywords":29841,"excerpt":29842},"modbus node-red, node-red modbus integration, node-red modbus tcp, modbus best practices, node-red industrial automation",{"type":12,"value":29843},[29844],[15,29845,29630],{},"\u002Fblog\u002F2023\u002F05\u002Fintegrating-modbus-with-node-red",{"title":29624,"description":29838},{"loc":29846},"blog\u002F2023\u002F05\u002Fintegrating-modbus-with-node-red","Integrate Modbus with Node-RED",[8650,138,7457,29852],"modbus","Integrating Modbus with Node-RED requires more than just connecting nodes industrial reliability demands watchdog timers for auto-recovery, conservative poll rates to avoid overloading legacy networks, grouped coil\u002Fregister addressing for efficiency, and correct handling of multi-register data types like 32-bit floats. This article walks through each best practice with example flows and node configurations.","77YhHHX8TKYaGPCWTj0LRN0T87yXH75wtlWMtlb58lM",{"id":29856,"title":29857,"authors":29858,"body":29859,"cta":3,"date":30165,"description":30166,"extension":116,"image":30167,"lastUpdated":6064,"meta":30168,"navigation":129,"path":30180,"seo":30181,"sitemap":30182,"stem":30185,"subtitle":30186,"tags":30187,"tldr":3,"video":3,"__hash__":30188},"blog\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node.md","Chat GPT in Node-RED Function Nodes",[8417,6225],{"type":12,"value":29860,"toc":30160},[29861,29877,29881,29900,29912,29915,29918,29996,30002,30005,30055,30058,30063,30071,30075,30082,30119,30123,30131,30134,30154,30157],[15,29862,29863,29864,29869,29870,29873,29874,29],{},"Recently we ",[63,29865,29868],{"href":29866,"rel":29867},"https:\u002F\u002Fwww.linkedin.com\u002Fposts\u002Fflowforge_chatgpt-with-node-red-function-nodes-activity-7052725869684953088-2yOA?utm_source=share&utm_medium=member_desktop",[67],"posted a demo of ChatGPT integration in a Node-RED function node","\nonto our social media accounts. We have now ",[63,29871,29872],{"href":22983,"target":29324},"open-sourced"," this for all to play with, and ",[19,29875,29876],{},"welcome any and all contributions",[31,29878,29880],{"id":29879},"how-it-works-prompt-engineering","How it Works - Prompt Engineering",[15,29882,29883,29884,29889,29890,29895,29896,29899],{},"OpenAI make a collection of their ",[63,29885,29888],{"href":29886,"rel":29887},"https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fmodels",[67],"Generative AI models"," available\nvia an API. We are wrapping OpenAI's ",[63,29891,29894],{"href":29892,"rel":29893},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fopenai",[67],"node.js module",", and in particular\nusing the ",[924,29897,29898],{},"openai.createChatCompletion()"," functionality. For this API, you provide a chat history, and ChatGPT will\nrespond with the next entry in that conversation.",[15,29901,29902,29903,853,29906,9079,29909,29],{},"In order to \"train\" ChatGPT for our use case of populating Node-RED function nodes, we first tried a collection of prompts, defining specific\nrequirements for the contents, e.g. ",[170,29904,29905],{},"\"Always write Javascript\"",[170,29907,29908],{},"\"Never include the wrapping function definition\"",[170,29910,29911],{},"\"Assume the input is always msg\"",[15,29913,29914],{},"It turns out though, that we were over-engineering it, we were not getting reliable results and ended up\nrealising that ChatGPT's existing knowledge of Node-RED was sufficient such that we could use that as a prompt:",[15,29916,29917],{},"Here's what we settled on:",[1756,29919,29921],{"className":1758,"code":29920,"language":1760,"meta":103,"style":103},"messages: [\n    {role: \"system\", content: \"always respond with content for a Node-RED function node, and don't add any commentary, always use const or let instead of var. Always return msg, unless told otherwise.\"},\n    {role: \"user\", content: prompt}\n],\n",[924,29922,29923,29932,29965,29990],{"__ignoreMap":103},[1107,29924,29925,29928,29930],{"class":1765,"line":1766},[1107,29926,29927],{"class":10543},"messages",[1107,29929,992],{"class":1783},[1107,29931,1937],{"class":1779},[1107,29933,29934,29936,29939,29941,29943,29946,29948,29950,29953,29955,29957,29960,29962],{"class":1765,"line":107},[1107,29935,1943],{"class":1783},[1107,29937,29938],{"class":1946},"role",[1107,29940,992],{"class":1783},[1107,29942,1963],{"class":1783},[1107,29944,29945],{"class":1966},"system",[1107,29947,1970],{"class":1783},[1107,29949,1955],{"class":1783},[1107,29951,29952],{"class":1946}," content",[1107,29954,992],{"class":1783},[1107,29956,1963],{"class":1783},[1107,29958,29959],{"class":1966},"always respond with content for a Node-RED function node, and don't add any commentary, always use const or let instead of var. Always return msg, unless told otherwise.",[1107,29961,1970],{"class":1783},[1107,29963,29964],{"class":1783},"},\n",[1107,29966,29967,29969,29971,29973,29975,29977,29979,29981,29983,29985,29988],{"class":1765,"line":294},[1107,29968,1943],{"class":1783},[1107,29970,29938],{"class":1946},[1107,29972,992],{"class":1783},[1107,29974,1963],{"class":1783},[1107,29976,16974],{"class":1966},[1107,29978,1970],{"class":1783},[1107,29980,1955],{"class":1783},[1107,29982,29952],{"class":1946},[1107,29984,992],{"class":1783},[1107,29986,29987],{"class":1779}," prompt",[1107,29989,8016],{"class":1783},[1107,29991,29992,29994],{"class":1765,"line":104},[1107,29993,1858],{"class":1779},[1107,29995,9079],{"class":1783},[15,29997,29998,29999,30001],{},"Here we send a ",[924,30000,29945],{}," prompt in order to setup ChatGPT, and then follow that immediately with whatever the user has typed.\nFrom our (limited) testing, this has given us fairly reliable results.",[15,30003,30004],{},"Breaking this prompt down:",[39,30006,30007,30025,30033,30041],{},[42,30008,30009,30014,30015,30018,30019,30021,30022,30024],{},[170,30010,30011],{},[19,30012,30013],{},"\"Always respond with content for a Node-RED function node\"",": Ensured no surrounding ",[924,30016,30017],{},"function () {}"," definition and set expectations that the function would deal with a ",[924,30020,14911],{}," and likely ",[924,30023,1227],{}," object.",[42,30026,30027,30032],{},[170,30028,30029],{},[19,30030,30031],{},"\"Don't add any commentary\"",": ChatGPT likes to, well, chat. It would always return raw text justifying decisions, etc. Here, we just wanted the code.",[42,30034,30035,30040],{},[170,30036,30037],{},[19,30038,30039],{},"\"Always use const or let instead of var\"",": This was Steve being picky.",[42,30042,30043,30048,30049,30051,30052,30054],{},[170,30044,30045],{},[19,30046,30047],{},"\"Always return msg, unless told otherwise\"",": We found this wasn't mostly required, but occasionally it would try to return a different variable, and we'd lose context of ",[924,30050,1227],{},", or other data stored in ",[924,30053,14911],{},". So this just made sure we had the consistency.",[15,30056,30057],{},"The response from this API call is then populated into the contents of the active tab in the function node:",[165,30059],{"width":30060,"alt":30061,"src":30062},1728,"Screenshot 2023-04-21 at 16 08 47","https:\u002F\u002Fuser-images.githubusercontent.com\u002F99246719\u002F233671631-fefa36c1-6db4-4392-a057-314c16fd91b7.png",[15,30064,30065,30066,29],{},"In order to use it yourself, you will need a ",[63,30067,30070],{"href":30068,"rel":30069},"https:\u002F\u002Fplatform.openai.com\u002Faccount\u002Fapi-keys",[67],"valid API Key from OpenAI",[31,30072,30074],{"id":30073},"additional-features","Additional Features",[15,30076,30077,30078,30081],{},"This was built in about a day by Steve and Joe, and we had plenty of ideas on what we'd like to add to it. We've\n",[63,30079,29872],{"href":22983,"rel":30080},[67]," it, and will add these as issues to the repo, but if anyone want so take a stab at contributing - that'd be most welcome!",[39,30083,30084,30099,30109],{},[42,30085,30086,30094,30095,30098],{},[19,30087,30088,30089,30093],{},"Insert at Cursor (",[63,30090,28906],{"href":30091,"rel":30092},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F11",[67],"):"," Currently, the Ask GPT call will replace ",[170,30096,30097],{},"all"," of the content of that tab. Would be great\nto have the code insert wherever the cursor last was in order to add to existing code.",[42,30100,30101,30108],{},[19,30102,30103,30104,30093],{},"Retain Conversation History (",[63,30105,28906],{"href":30106,"rel":30107},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F12",[67]," Each time a new prompt is provided by the Node-RED user, we send a fresh conversation to OpenAI,\nmeaning that knowledge of previously asked questions are not retained.",[42,30110,30111,30118],{},[19,30112,30113,30114,30093],{},"Client side ChatGPT Config (",[63,30115,28906],{"href":30116,"rel":30117},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F13",[67]," Currently, when you add a new \"function-gpt\" node you need to select the ChatGTP\nConfig node and click \"Deploy\" before you can ask it a question. Our ChatGPT interaction operates server-side (to\nprotect your API key), so Node-RED needs that in the runtime first, before a call to ChatGPT can be made. Ideally,\nwe'd be smarter here and pass client-side creds along with the call such that we can use any changes made by the\nuser at the time of the call.",[31,30120,30122],{"id":30121},"flowfuse-assistant-no-api-keys-required","FlowFuse Assistant - No API Keys Required!",[15,30124,30125,30126,30130],{},"Great news! You no longer need to manage OpenAI API keys or configure ChatGPT nodes. The ",[63,30127,30129],{"href":30128},"\u002Fdocs\u002Fuser\u002Fexpert\u002F","FlowFuse Assistant"," is now built directly into Node-RED on FlowFuse Cloud, making AI-powered development even easier.",[15,30132,30133],{},"Available on FlowFuse Cloud, the Assistant offers:",[39,30135,30136,30142,30148],{},[42,30137,30138,30141],{},[19,30139,30140],{},"Quick Function Node Creation",": Add function nodes to your flow without dragging from the palette",[42,30143,30144,30147],{},[19,30145,30146],{},"In-line Code Generation",": Generate JavaScript code for function nodes, JSON for JSON editors, and Vue.js for FlowFuse Dashboard ui-template widgets",[42,30149,30150,30153],{},[19,30151,30152],{},"Flow Explainer",": Select nodes and click \"Explain Flows\" to understand what they do",[15,30155,30156],{},"FlowFuse Assistant helps developers work faster and smarter with Node-RED. [Start your free trial]({% include \"sign-up-url.njk\" %}) to experience AI-powered Node-RED development on FlowFuse Cloud.",[2582,30158,30159],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":30161},[30162,30163,30164],{"id":29879,"depth":107,"text":29880},{"id":30073,"depth":107,"text":30074},{"id":30121,"depth":107,"text":30122},"2023-05-02","Discover how ChatGPT integrates with Node-RED function nodes, enabling automated code generation. Explore the prompt engineering process and additional features.","\u002Fimages\u002Fblog\u002Ftile-chatgpt-fcn-node.jpg",{"excerpt":30169},{"type":12,"value":30170},[30171],[15,30172,29863,30173,29869,30176,29873,30178,29],{},[63,30174,29868],{"href":29866,"rel":30175},[67],[63,30177,29872],{"href":22983,"target":29324},[19,30179,29876],{},"\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node",{"title":29857,"description":30166},{"loc":30180,"images":30183},[30184],{"loc":30062},"blog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node","New Node-RED function with embedded ChatGPT is now open-sourced and available to use!",[8650,138,137,7457,22403,2784],"Yu5S1tx3B1sfZdRnsMPUdMZVRQ1TuP3iVWY1HoBI-bU",{"id":30190,"title":29145,"authors":30191,"body":30192,"cta":3,"date":30341,"description":30342,"extension":116,"image":29183,"lastUpdated":6064,"meta":30343,"navigation":129,"path":30348,"seo":30349,"sitemap":30350,"stem":30351,"subtitle":30352,"tags":30353,"tldr":3,"video":3,"__hash__":30355},"blog\u002Fblog\u002F2023\u002F04\u002F3-quick-node-red-tips-6.md",[19494],{"type":12,"value":30193,"toc":30333},[30194,30196,30200,30203,30207,30210,30216,30219,30223,30226,30232,30235,30241,30245,30248,30251,30254,30260,30263,30269,30272,30278,30282,30285,30291,30294,30300,30303,30309,30313],[15,30195,28944],{},[399,30197,30199],{"id":30198},"_1-subflows","1. Subflows",[15,30201,30202],{},"Subflows are a great way to reuse sections of your flows. Once you have created a subflow, it can easily be dropped into your workspace one or more times.",[1165,30204,30206],{"id":30205},"why-use-subflows","Why use subflows?",[15,30208,30209],{},"Without using a subflow, you can copy and paste a flow into each place you need to use it. This takes up quite a bit of workspace, and makes it harder to update your flow in the future as you'll have to update each copy.",[15,30211,30212],{},[165,30213],{"alt":30214,"src":30215,"title":30214},"Duplication of the flow","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fno-subflow.png",[15,30217,30218],{},"If we instead put the flow into a subflow we'll save a lot of workspace and it will be easier to update the reused sections of the flow if we need to in the future.",[1165,30220,30222],{"id":30221},"creating-a-subflow","Creating a subflow",[15,30224,30225],{},"You can create a subflow using the burger menu in the top right corner of Node-RED, select Subflows, then Create Subflow. Lay out your subflow, making sure you create an input and output. You can even have more than one output if you want.",[15,30227,30228],{},[165,30229],{"alt":30230,"src":30231,"title":30230},"Contents of the subflow","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fsubflow.png",[15,30233,30234],{},"You can now drop the subflow into your workspace as needed, saving space and making it easier to manage changes to your flow.",[15,30236,30237],{},[165,30238],{"alt":30239,"src":30240,"title":30239},"Using the subflow to reduce duplication of flows","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fusing-the-subflow.png",[399,30242,30244],{"id":30243},"_2-link-nodes","2. Link Nodes",[15,30246,30247],{},"Link Nodes allow you to separate your flows into distinct sections. The wires between the link nodes are not visible until you select that part of the flow. You can also link flows on different tabs together.",[15,30249,30250],{},"Formatting your flows into distinct sections using link nodes can make it easier to read and update your work.",[15,30252,30253],{},"To use the link node, drag a link in and out node into your flow's workspace. Now draw a wire as you usually would to link to two nodes together. You should see a link between the nodes but it only shows when you have the link nodes selected.",[15,30255,30256],{},[165,30257],{"alt":30258,"src":30259,"title":30258},"Linking two link nodes together","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fwiring-link-nodes.gif",[15,30261,30262],{},"In this example below, the first and second flows have the same nodes and functionality. In the second image of the workspace I've split the flow into specific groups of nodes.",[15,30264,30265],{},[165,30266],{"alt":30267,"src":30268,"title":30267},"A flow without link nodes","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fflow-without-link-nodes.png",[15,30270,30271],{},"It's easier to read and understand the flow once it's split up using the link nodes and groups.",[15,30273,30274],{},[165,30275],{"alt":30276,"src":30277,"title":30276},"The same flow as above, now split up using link nodes","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fflow-with-link-nodes.png",[399,30279,30281],{"id":30280},"_3-range-node","3. Range Node",[15,30283,30284],{},"Sometimes you might need to map one numbering scale onto another. For example, where a user has selected a value between 0 and 10 but you want to use and store their response as a percentage. The Range node makes this task very easy.",[15,30286,30287],{},[165,30288],{"alt":30289,"src":30290,"title":30289},"Example of using the range node","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fflow-using-range.png",[15,30292,30293],{},"To configure the node, set it up as follows:",[15,30295,30296],{},[165,30297],{"alt":30298,"src":30299,"title":30298},"Configuration of the range node","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Frange-config.png",[15,30301,30302],{},"You should now see that the input values are translated to the appropriate value out of 100.",[15,30304,30305],{},[165,30306],{"alt":30307,"src":30308,"title":30307},"The range note in use","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Frange-demo.gif",[15,30310,29135,30311,29139],{},[63,30312,12885],{"href":29138},[15,30314,30315,30317,30319,30321,30323,30325,30327,30329,30331],{},[63,30316,29151],{"href":29150},[5368,30318],{},[63,30320,29157],{"href":29156},[5368,30322],{},[63,30324,29163],{"href":29162},[5368,30326],{},[63,30328,29169],{"href":29168},[5368,30330],{},[63,30332,29175],{"href":29174},{"title":103,"searchDepth":104,"depth":104,"links":30334},[30335,30339,30340],{"id":30198,"depth":294,"text":30199,"children":30336},[30337,30338],{"id":30205,"depth":104,"text":30206},{"id":30221,"depth":104,"text":30222},{"id":30243,"depth":294,"text":30244},{"id":30280,"depth":294,"text":30281},"2023-04-20","Learn how to streamline your Node-RED projects with these essential tips: Subflows, Link Nodes, and Range Node for efficient workflow organization.",{"excerpt":30344},{"type":12,"value":30345},[30346],[15,30347,28944],{},"\u002Fblog\u002F2023\u002F04\u002F3-quick-node-red-tips-6",{"title":29145,"description":30342},{"loc":30348},"blog\u002F2023\u002F04\u002F3-quick-node-red-tips-6","Save yourself time when working on Node-RED with these three tips.",[138,30354,7457],"node-red subflow","zbjlyCGZrnt1FelOIp_DAi162HKXwthU3yVidwpm3ak",{"id":30357,"title":30358,"authors":30359,"body":30360,"cta":3,"date":30341,"description":30378,"extension":116,"image":30379,"lastUpdated":3,"meta":30380,"navigation":129,"path":30385,"seo":30386,"sitemap":30387,"stem":30388,"subtitle":30389,"tags":30390,"tldr":3,"video":3,"__hash__":30391},"blog\u002Fblog\u002F2023\u002F04\u002Fhannover-messe.md","FlowFuse's visit to Hannover Messe 2023",[18092],{"type":12,"value":30361,"toc":30376},[30362,30365,30373],[15,30363,30364],{},"\"Do you use Node-RED?\" This simple question became our favorite conversation opener as ZJ and I attended Hannover Messe, the world's leading industrial trade fair.",[15,30366,30367,30368,30372],{},"To our delight, the answer was almost always \"Yes.\" This allowed us to dive into truly fruitful discussions with Node-RED experts, exploring the latest industry trends, connecting with potential partners, and sharing our vision for the future of Node-RED in the manufacturing sector. During our visit, we had the pleasure of engaging with several vendors, including Bosch Rexroth, Siemens, Wago, Weidmüller, RevolutionPi, and more. It was genuinely inspiring to see the widespread adoption and usage of Node-RED across the entire industry. See also our ",[63,30369,30371],{"href":30370},"\u002Fblog\u002F2023\u002F03\u002Fintegration-platform-for-edge-computing\u002F#the-standard-for-edge-computing-and-plcs","arcticle"," about the adoption of Node-RED for PLCs and Gateways.",[15,30374,30375],{},"FlowFuse's innovative solutions, such as remote deployment of Node-RED instances, seamless updates, effortless rollbacks, and built-in security, are already addressing many challenges faced by Node-RED users in the industry today. The positive feedback we received at Hannover Messe has bolstered our commitment to making Node-RED more accessible and production-ready for industrial and enterprise scenarios. As we forge ahead, we are eager to collaborate with more partners, learn from industry leaders, and remain at the forefront of Node-RED development. Together, we can build a more connected, efficient, and innovative manufacturing industry. With each new booth we visited and every conversation we had, it became increasingly clear that Node-RED and FlowFuse are well on their way to becoming an integral part of the industrial landscape.",{"title":103,"searchDepth":104,"depth":104,"links":30377},[],"FlowFuse's experience at Hannover Messe, discussing their interactions with Node-RED users and vendors.","\u002Fblog\u002F2023\u002F04\u002Fimages\u002FHMI2023.png",{"excerpt":30381},{"type":12,"value":30382},[30383],[15,30384,30364],{},"\u002Fblog\u002F2023\u002F04\u002Fhannover-messe",{"title":30358,"description":30378},{"loc":30385},"blog\u002F2023\u002F04\u002Fhannover-messe","A recap of FlowFuse's visit to Hannover Messe",[8650,10,138,137],"X-wlUPSN3hSjyVJhI4OyT9I5fVQkE7XOri1hveoEUG8",{"id":30393,"title":30394,"authors":30395,"body":30396,"cta":3,"date":30480,"description":30481,"extension":116,"image":30482,"lastUpdated":3,"meta":30483,"navigation":129,"path":30488,"seo":30489,"sitemap":30490,"stem":30491,"subtitle":30492,"tags":30493,"tldr":3,"video":3,"__hash__":30494},"blog\u002Fblog\u002F2023\u002F04\u002Fnodered-community-health.md","Node-RED Community Health",[29200],{"type":12,"value":30397,"toc":30478},[30398,30401,30406,30415,30420,30429,30434,30443,30448,30462,30467,30475],[15,30399,30400],{},"It is often a challenge to measure the health of an open source project, like Node-RED. Individuals can download and use Node-RED without any indication or feedback to their ongoing satisfaction or usage. However, it is still interesting to look at a variety of metrics to understand the size of the Node-RED community.",[15,30402,30403],{},[19,30404,30405],{},"GitHub Stars",[15,30407,30408,30409,30414],{},"A popular method for demonstrating popularity of an open source project are GitHub stars. Node-RED has over 16K stars and when ",[63,30410,30413],{"href":30411,"rel":30412},"https:\u002F\u002Fsynodus.com\u002Fblog\u002Flow-code\u002Fopen-source-low-code-platforms\u002F",[67],"compared to other low-code platforms"," on GitHub has a strong showing. FWIW, GitHub stars are open to gaming so it is not a great long-term indicator of community health and engagement.",[15,30416,30417],{},[19,30418,30419],{},"Node-RED Library",[15,30421,30422,30423,30428],{},"At the core of the Node-RED community is the library of nodes and flows that have been developed by community members. The current library has over ",[63,30424,30427],{"href":30425,"rel":30426},"https:\u002F\u002Fflows.nodered.org\u002Fsearch?type=node&sort=downloads",[67],"4300 nodes available",", some of the more popular nodes are downloaded more than 10K times per week. The large library of community nodes supports a wide range of protocols, data sources, data stores, and much more. This makes Node-RED more relevant and useful to many more potential users.",[15,30430,30431],{},[19,30432,30433],{},"Node-RED Website Traffic",[15,30435,30436,30437,30442],{},"In the last year, Node-RED has had over 1.9 million unique visitors to the ",[63,30438,30441],{"href":30439,"rel":30440},"https:\u002F\u002Fnodered.org\u002F",[67],"nodered.org"," website, over 160K on a monthly basis. The most popular pages, after the home page, are the getting started pages. This demonstrates a strong interest in learning more about how to use Node-RED.",[15,30444,30445],{},[19,30446,30447],{},"NPM Downloads & Docker Pull Requests",[15,30449,30450,30451,30456,30457,29],{},"Node-RED is installed by lots of people using Docker and NPM. The project averages over 500K\u002Fmonth pull requests from ",[63,30452,30455],{"href":30453,"rel":30454},"https:\u002F\u002Fhub.docker.com\u002Fr\u002Fnodered\u002Fnode-red",[67],"Docker Hub"," and 100K\u002Fmonth pull requests on ",[63,30458,30461],{"href":30459,"rel":30460},"https:\u002F\u002Fnpm-stat.com\u002Fcharts.html?package=node-red&from=2017-03-22&to=2023-03-22",[67],"NPM",[15,30463,30464],{},[19,30465,30466],{},"Node-RED Forums",[15,30468,175,30469,30474],{},[63,30470,30473],{"href":30471,"rel":30472},"https:\u002F\u002Fdiscourse.nodered.org\u002F",[67],"Node-RED community forum"," is the go to place to get community support. It is a very popular community with over 900K page views per month. The amazing thing is that a very large percentage of questions receive replies from other community members. A great sign of a healthy community.",[15,30476,30477],{},"Overall, the Node-RED community is large, healthy and engaged.",{"title":103,"searchDepth":104,"depth":104,"links":30479},[],"2023-04-13","A summary of the Node-RED community metrics that demostrates a large and engaging community.","\u002Fimages\u002Fblog\u002Ftile-node-red-community-metrics.jpg",{"excerpt":30484},{"type":12,"value":30485},[30486],[15,30487,30400],{},"\u002Fblog\u002F2023\u002F04\u002Fnodered-community-health",{"title":30394,"description":30481},{"loc":30488},"blog\u002F2023\u002F04\u002Fnodered-community-health","Node-RED community metrics",[8650,138,137,136],"hbHJgFL-AmfTdx2dbGdqo0KyXrf-m-iUNk11cJd6BC0",{"id":30496,"title":30497,"authors":30498,"body":30499,"cta":30810,"date":30813,"description":30814,"extension":116,"image":30815,"lastUpdated":6203,"meta":30816,"navigation":129,"path":30822,"seo":30823,"sitemap":30824,"stem":30825,"subtitle":30826,"tags":30827,"tldr":30829,"video":3,"__hash__":30830},"blog\u002Fblog\u002F2023\u002F04\u002Fsecuring-node-red-in-production.md","Securing Node-RED (2026)",[19494],{"type":12,"value":30500,"toc":30804},[30501,30504,30507,30510,30514,30517,30522,30525,30528,30531,30536,30539,30546,30555,30560,30563,30569,30572,30575,30580,30583,30589,30592,30598,30601,30604,30609,30612,30618,30621,30624,30630,30633,30639,30642,30645,30649,30652,30655,30663,30672,30675,30678,30687,30695,30698,30703,30708,30713,30718,30723,30728,30733,30738,30741,30749,30752,30758,30761,30766,30769,30775,30783,30787,30790,30793,30796,30799,30801],[15,30502,30503],{},"Node-RED is very easy to get up and running. Whether you run it locally, in Docker, on a Raspberry Pi, or on a service such as FlowFuse Cloud you can have a project up and running in minutes.",[15,30505,30506],{},"One thing that can get overlooked is the security of Node-RED. From personal experience, the first few times I installed Node-RED I was more focussed on the possibilities of what I could do with this new tool than I was keeping my projects secure.",[15,30508,30509],{},"In this article I’m going to look at some easy ways to make your Node-RED project more secure, even when first learning about it in a hobby environment.",[31,30511,30513],{"id":30512},"protecting-access-from-your-lan-to-node-red","Protecting access from your LAN to Node-RED",[15,30515,30516],{},"Once you have an instance of Node-RED running it can usually be accessed from anywhere on your LAN (local area network) by pointing a web browser to the relevant IP address and port.",[15,30518,30519],{},[924,30520,30521],{},"http:\u002F\u002F192.168.0.3:1880",[15,30523,30524],{},"With a URL similar to the one above, depending on your specific network and Node-RED configuration, anyone on your LAN can view but more importantly edit your flows. This can be really useful when you are first learning about Node-RED but it’s always a good idea to get into the habit of locking down access to the editor, even if you trust everyone who can access your LAN.",[15,30526,30527],{},"One of the easiest ways to protect your flows is to add a username and password to your Node-RED instance.",[15,30529,30530],{},"The first step is to find your Node-RED settings.js file. It's not always in the same place but on a default Debian Linux installation it can be found in this directory.",[15,30532,30533],{},[924,30534,30535],{},"cd ~\u002F.node-red",[15,30537,30538],{},"If you list that directory you should now see something like this:",[15,30540,30541],{},[165,30542],{"alt":30543,"src":30544,"title":30545},"Where your settings file should show","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fls.png","Where your settings.js should show",[15,30547,30548,30549,30554],{},"We now need to edit settings.js, I'm going to use my favourite text editor, ",[63,30550,30553],{"href":30551,"rel":30552},"https:\u002F\u002Fwww.nano-editor.org\u002F",[67],"Nano"," to do that.",[15,30556,30557],{},[924,30558,30559],{},"nano settings.js",[15,30561,30562],{},"We now need to find and edit the following section of the settings file:",[15,30564,30565],{},[165,30566],{"alt":30567,"src":30568,"title":30567},"The settings file before being edited","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fwithout-password.png",[15,30570,30571],{},"For this example, I'm going to add a password and uncomment the relevant section of the settings file, you could also change the username for additional security.",[15,30573,30574],{},"To create the password we'll need to use a command line tool which is included in Node-RED. Open a second terminal then run this command:",[15,30576,30577],{},[924,30578,30579],{},"node-red admin hash-pw",[15,30581,30582],{},"Put in your new password, I'll use the password 'flowforge' in this example. The tool returns your password in a hashed format:",[15,30584,30585],{},[165,30586],{"alt":30587,"src":30588,"title":30587},"The Node-RED tool outputs the hashed password","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fpassword.png",[15,30590,30591],{},"We can now return to the other terminal window, uncomment the section then paste in the new password, this is how it looks for me:",[15,30593,30594],{},[165,30595],{"alt":30596,"src":30597,"title":30596},"The settings file with the relevant section uncommented and the password set","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fwith-password.png",[15,30599,30600],{},"We can now save and exit out of the settings file.",[15,30602,30603],{},"The last step is to restart Node-RED, I'm using Debian so the command is:",[15,30605,30606],{},[924,30607,30608],{},"node-red-restart",[15,30610,30611],{},"Now, when we try to access Node-RED I will need to provide a username and password.",[15,30613,30614],{},[165,30615],{"alt":30616,"src":30617,"title":30616},"Using the username and password to login to Node-RED","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Flogin.gif",[15,30619,30620],{},"You might also want to consider turning off the editor interface once you are happy with your flows. This can make it a little harder to make changes to your project but it also gives you peace of mind that nobody has accidentally or deliberately changed your flows. You can turn off the editor interface as follows.",[15,30622,30623],{},"Edit your settings.js file as explained above, look for the following section:",[15,30625,30626],{},[165,30627],{"alt":30628,"src":30629,"title":30628},"The setting file before turning off the editor","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Feditor-on.png",[15,30631,30632],{},"All you need to do is uncomment the bottom line then change the value from false to true, once done it should look something like this:",[15,30634,30635],{},[165,30636],{"alt":30637,"src":30638,"title":30637},"The setting file after turning off the editor","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Feditor-off.png",[15,30640,30641],{},"Now restart Node-RED as covered above, then try accessing your Node-RED instance again. You will no longer be able to edit or view your flows.",[15,30643,30644],{},"Using these two features, we now have much better control over who can access the design interface for Node-RED.",[31,30646,30648],{"id":30647},"traffic-to-your-node-red-instance-is-unencrypted","Traffic to your Node-RED instance is unencrypted",[15,30650,30651],{},"Hopefully, we all know the importance of encrypting your connections between devices to stop people intercepting your traffic. This isn't a huge concern when working on your home LAN but what if you want to access your Node-RED instance from a remote location?",[15,30653,30654],{},"There are two obvious options, HTTPS, and a VPN (Virtual Private Network).",[15,30656,30657,30658,30662],{},"We could setup your Node-RED traffic to run over HTTPS, this solution ensures that all traffic to and from your Node-RED is encrypted. The downside to this approach is it's quite complex to set up. We will need to have a domain name, open up ports on our LAN's firewall, use a HTTPS certificate provider and then make sure we remember to renew the certificates as needed. It's doable if you are comfortable with those concepts (I covered how to do this as part of my blog ",[63,30659,30661],{"href":30660},"\u002Fblog\u002F2022\u002F12\u002Fflowforge-gcp-https-set-up\u002F","hosting FlowFuse on Google Cloud",") but there is an easier way to get started, using a VPN.",[15,30664,30665,30666,30671],{},"A VPN provides a lot of security advantages depending on which you are using and how it is configured. To secure my traffic I'm going to use a great service call ",[63,30667,30670],{"href":30668,"rel":30669},"https:\u002F\u002Ftailscale.com\u002F",[67],"Tailscale"," which is free for personal projects.",[15,30673,30674],{},"I'm going to install Tailscale on the Raspberry Pi I'm running Node-RED on as well as any other devices I want to access my project from. Once that's done I can access Node-RED from anywhere with internet access but more importantly the traffic to and from my devices is also encrypted.",[15,30676,30677],{},"Before we start, it's important to remember that a VPN is only as secure as the company who runs it. You should always consider if you trust the VPN provider as they could potentially access your devices. I trust Tailscale but please do your own research before using a VPN provider.",[15,30679,30680,30681,30686],{},"The first step we need to take is creating a Tailscale account, ",[63,30682,30685],{"href":30683,"rel":30684},"https:\u002F\u002Flogin.tailscale.com\u002Fstart",[67],"you can sign up for free here",". We next need to add our devices to our VPN using their software, I'm installing Tailscale on my Apple laptop, Google phone as well as the Raspberry Pi I'm running Node-RED on.",[15,30688,30689,30690,29],{},"The install process is really easy, even on the Pi running Raspbian the steps you need to take are well explained in the ",[63,30691,30694],{"href":30692,"rel":30693},"https:\u002F\u002Ftailscale.com\u002Fdownload\u002Flinux\u002Fdebian-bullseye",[67],"Tailscale docs",[15,30696,30697],{},"For the Pi, these are the commands we need to run.",[271,30699,30700],{},[42,30701,30702],{},"Add Tailscale to the Apt package manager.",[15,30704,30705],{},[924,30706,30707],{},"curl -fsSL https:\u002F\u002Fpkgs.tailscale.com\u002Fstable\u002Fdebian\u002Fbullseye.noarmor.gpg | sudo tee \u002Fusr\u002Fshare\u002Fkeyrings\u002Ftailscale-archive-keyring.gpg >\u002Fdev\u002Fnull",[15,30709,30710],{},[924,30711,30712],{},"curl -fsSL https:\u002F\u002Fpkgs.tailscale.com\u002Fstable\u002Fdebian\u002Fbullseye.tailscale-keyring.list | sudo tee \u002Fetc\u002Fapt\u002Fsources.list.d\u002Ftailscale.list",[15,30714,30715],{},[924,30716,30717],{},"sudo apt-get update",[271,30719,30720],{"start":107},[42,30721,30722],{},"Install Tailscale",[15,30724,30725],{},[924,30726,30727],{},"sudo apt-get install tailscale",[271,30729,30730],{"start":294},[42,30731,30732],{},"Start Tailscale and connect your device",[15,30734,30735],{},[924,30736,30737],{},"sudo tailscale up",[15,30739,30740],{},"After running the last command, we need to follow the on screen prompts to link our devices to your VPN.",[15,30742,30743,30744,29],{},"One last thing which you might want to consider doing, every few months you will need to reconnect your devices to your VPN, if you are only going to be accessing your Node-RED device over the VPN you should consider ",[63,30745,30748],{"href":30746,"rel":30747},"https:\u002F\u002Ftailscale.com\u002Fkb\u002F1028\u002Fkey-expiry\u002F",[67],"disabling your Tailscale key expiry",[15,30750,30751],{},"OK, now we've got our devices all connected you should see something like this in the Tailscale dashboard.",[15,30753,30754],{},[165,30755],{"alt":30756,"src":30757,"title":30756},"Tailscale dashboard showing my three devices","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Ftailscale.png",[15,30759,30760],{},"I can now access Node-RED on my Pi from my laptop and phone by pointing a browser to the correct IP address (as shown in the image above) with the port for Node-RED:",[15,30762,30763],{},[924,30764,30765],{},"http:\u002F\u002F100.71.28.60:1880",[15,30767,30768],{},"I’ve now secured all traffic between my devices and Node-RED project, I can access Node-RED from anywhere on the internet.",[15,30770,30771],{},[165,30772],{"alt":30773,"src":30774,"title":30773},"Accessing Node-RED via the VPN","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fnr-via-vpn.png",[15,30776,30777,30778,30782],{},"If you follow these steps you should be on the right path to running a more secure Node-RED instance. There is a lot more you can do and I recommend you read the ",[63,30779,30781],{"href":29785,"rel":30780},[67],"relevant docs on the Node-RED"," website to gain some more ideas.",[31,30784,30786],{"id":30785},"what-about-hosting-node-red-on-a-cloud-solution-such-as-flowfuse","What about hosting Node-RED on a cloud solution such as FlowFuse?",[15,30788,30789],{},"In this article, I've focussed on hosting Node-RED on a Pi on your own LAN but if you use FlowFuse Cloud to host Node-RED the solutions discussed above are either ready out of the box or are not needed.",[15,30791,30792],{},"By default, the Node-RED editor is secured using your FlowFuse user credentials. You can also enable SSO to enhance account security and easily grant access to team members. With role-based access control, you can further protect your flows by managing who can view or edit them.",[15,30794,30795],{},"All traffic to FlowFuse and your Node-RED instances is protected by HTTPS. FlowFuse has set up the domain name and manages the certificates, so you can spend time on your flows rather than configuring security. Additionally, remote device access is secured through encrypted tunnels, providing comprehensive protection for your deployments.",[15,30797,30798],{},"FlowFuse has a [free trial]({% include \"sign-up-url.njk\" %}) if you'd like to see how we've made secure hosting of Node-RED easy.",[31,30800,1425],{"id":1424},[15,30802,30803],{},"How ever you host Node-RED, it's a great idea to get into good security practices as early as possible to ensure that no unsecured Node-RED instances are exposed to the internet. I hope some of the tips above help you get started down the path to creating more secure Node-RED projects.",{"title":103,"searchDepth":104,"depth":104,"links":30805},[30806,30807,30808,30809],{"id":30512,"depth":107,"text":30513},{"id":30647,"depth":107,"text":30648},{"id":30785,"depth":107,"text":30786},{"id":1424,"depth":107,"text":1425},{"type":748,"title":30811,"description":30812},"Secure Node-RED Without the Complexity","FlowFuse manages HTTPS, authentication, and remote access security for you out of the box, so you can focus on building flows instead of configuring infrastructure.","2023-04-05","Explore essential steps for securing Node-RED projects, from LAN access control to encrypted traffic, ensuring robust protection for your projects.","\u002Fblog\u002F2023\u002F04\u002Fimages\u002Fsecurity-header.png",{"keywords":30817,"excerpt":30818},"securing node-red, node-red security, node-red password, node-red https, node-red vpn, node-red authentication",{"type":12,"value":30819},[30820],[15,30821,30503],{},"\u002Fblog\u002F2023\u002F04\u002Fsecuring-node-red-in-production",{"title":30497,"description":30814},{"loc":30822},"blog\u002F2023\u002F04\u002Fsecuring-node-red-in-production","Step-by-step guide for securing your Node-RED projects.",[8650,138,7457,30828],"securing node-red","This step-by-step guide covers the essential security measures for Node-RED deployments: adding username\u002Fpassword authentication via settings.js, restricting LAN access, enabling HTTPS with TLS certificates, and using FlowFuse Cloud as a managed alternative that handles these concerns automatically. Even hobby deployments benefit from these protections to prevent unauthorized access to the Node-RED editor.","6BcWNGMuEIfrnHRu6Gshqk33JH8J5WF-g0yqHX1KP6k",{"id":30832,"title":30833,"authors":30834,"body":30835,"cta":3,"date":31018,"description":31019,"extension":116,"image":31020,"lastUpdated":3,"meta":31021,"navigation":129,"path":31032,"seo":31033,"sitemap":31034,"stem":31035,"subtitle":31036,"tags":31037,"tldr":3,"video":3,"__hash__":31038},"blog\u002Fblog\u002F2023\u002F03\u002Fibmcloud-starter-removed.md","IBM Cloud removes Node-RED starter application",[8417],{"type":12,"value":30836,"toc":31009},[30837,30851,30854,30857,30860,30866,30869,30873,30881,30884,30892,30898,30906,30913,30920,30922,30930,30949,30953,30956,30960,30967,30971,30974,31001],[15,30838,30839,30840,30845,30846,29],{},"IBM Cloud has ",[63,30841,30844],{"href":30842,"rel":30843},"https:\u002F\u002Fwww.ibm.com\u002Fcloud\u002Fblog\u002Fannouncements\u002Fdeprecation-of-ibm-cloud-starter-kits",[67],"recently announced"," that they will no longer be providing their Cloud App Service Starter Kits, including the ",[63,30847,30850],{"href":30848,"rel":30849},"https:\u002F\u002Fdeveloper.ibm.com\u002Ftutorials\u002Fhow-to-create-a-node-red-starter-application\u002F",[67],"Node-RED Starter Application",[31,30852,30850],{"id":30853},"node-red-starter-application",[15,30855,30856],{},"If you're looking for an alternative place to get started with Node-RED then FlowFuse, founded by Node-RED co-creator Nick O'Leary, are here to help you with Cloud-hosted Node-RED as well as infrastructure and tooling to scale your Node-RED instances in production.",[15,30858,30859],{},"As an ex-IBM Employee myself, over the years, I've been very dependant upon the Node-RED Starter Application in IBM Cloud. I'd used it dozens of times with clients to showcase the value of Node-RED and how easy it is to spin up integrations between hardware devices, APIs and online services.",[15,30861,30862,30863,15283],{},"If you're now looking for somewhere to rely on in order to easily spin up new instance of Node-RED, then FlowFuse is the obvious answer. If you're completely new for Node-RED too, we can also help you there with our ",[63,30864,880],{"href":30865},"\u002Fblog\u002F2023\u002F01\u002Fgetting-started-with-node-red\u002F",[15,30867,30868],{},"You can sign up for a [free FlowFuse Cloud Account]({% include \"sign-up-url.njk\" %}) where you'll be given one small Node-RED instance for free, for your first month.",[31,30870,30872],{"id":30871},"integrations","Integrations",[15,30874,30875,30876,30880],{},"If you've seen the excellent ",[63,30877,30879],{"href":30848,"rel":30878},[67],"\"Create a Node-RED starter application\""," article on IBM Developer, you'll probably be looking to connect up to a Cloudant Instance, or other IBM Cloud Services. Don't worry. All of that is still available through Node-RED on FlowFuse.",[15,30882,30883],{},"You can install the relevant nodes in one of two places:",[271,30885,30886],{},[42,30887,30888,30891],{},[19,30889,30890],{},"Node-RED Palette Manager:"," Click \"Menu > Manage Palette > Install\". The menu is available via the icon in the top-right of your running Node-RED Instance)",[15,30893,30894],{},[165,30895],{"alt":30896,"src":30897,"title":30896},"Screenshot of Node-RED's Manage Palette menu","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fnr-manage-palette-cloudant.png",[271,30899,30900],{"start":107},[42,30901,30902,30905],{},[19,30903,30904],{},"FlowFuse Instance Settings:"," For a given Instance in FlowFuse, click \"Settings > Palette\". You can then define the npm module name and versions explicitely in the \"Installed Modules\" section",[15,30907,30908],{},[165,30909],{"alt":30910,"src":30911,"title":30912},"Screenshot of FlowFuse's \"Installed Modules\" option in Instance > Settings > Palette","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fff-installed-modules.png","Screenshot of FlowFuse's 'Installed Modules' option in Instance > Settings > Palette",[15,30914,30915,30916,30919],{},"It's also easy to setup ",[63,30917,30918],{"href":14312},"Environment Variables"," for Node-RED in FlowFuse for when you integrate with external services like APIs too.",[31,30921,10371],{"id":29779},[15,30923,30924,30925,30929],{},"As with IBM Cloud, FlowFuse makes it very easy to secure your Node-RED Applications. ",[63,30926,30928],{"href":30927},"\u002Fdocs\u002Fuser\u002Finstance-settings\u002F#security","FlowFuse offers three tiers of security"," options on your Node-RED Instances to secure any exposed HTTP routes on your Node-RED instance, e.g. REST API endpoints or Node-RED Dashboard.",[39,30931,30932,30937,30943],{},[42,30933,30934,30936],{},[19,30935,28674],{},": Anyone will be able to access the exposed routes.",[42,30938,30939,30942],{},[19,30940,30941],{},"Basic Auth",": Setup a single, dedicated username and password combination that is required in order to access the routes.",[42,30944,30945,30948],{},[19,30946,30947],{},"FlowFuse Credentials",": Visitors can use their FlowFuse username\u002Fpassword in order to access the endpoints. This also includes SSO if you have that configured for your FlowFoerge Team.",[31,30950,30952],{"id":30951},"migrating-existing-instances","Migrating Existing Instances",[15,30954,30955],{},"If you're looking to move your Node-RED applications from IBM Cloud, then you can do so through one of two options:",[399,30957,30959],{"id":30958},"node-red-tools-plugin","Node-RED Tools Plugin",[15,30961,30962,30963,30966],{},"You can use our Node-RED Tools plugin to migrate your flows and credentials over to FlowFuse. You can read the details in our ",[63,30964,13997],{"href":30965},"\u002Fdocs\u002Fmigration\u002Fintroduction\u002F",", which also includes instructions on how to export your environment variables too.",[399,30968,30970],{"id":30969},"manual-import","Manual Import",[15,30972,30973],{},"This will only enable you to import your Flows, not the associated credentials.",[271,30975,30976,30983,30986,30989,30992,30995],{},[42,30977,30978,30979,30982],{},"Export your existing ",[924,30980,30981],{},"flows.json"," from your IBM Cloud-hosted Node-RED instance by choosing \"Export > All Flows > Download\" within Node-RED",[42,30984,30985],{},"Create a new Application on FlowFuse",[42,30987,30988],{},"Once created, click the Node-RED instance that has been generated within your Application",[42,30990,30991],{},"Click \"Settings\"",[42,30993,30994],{},"Scroll down and select the \"Import Instance\" option",[42,30996,30997,30998,31000],{},"Choose your ",[924,30999,30981],{}," file that you downloaded earlier.",[15,31002,31003],{},[170,31004,31005,31006,29],{},"If you have any questions about the above, or more generally about Node-RED or FlowFuse, then please do reach out and ",[63,31007,12885],{"href":31008},"\u002Fcontact-us",{"title":103,"searchDepth":104,"depth":104,"links":31010},[31011,31012,31013,31014],{"id":30853,"depth":107,"text":30850},{"id":30871,"depth":107,"text":30872},{"id":29779,"depth":107,"text":10371},{"id":30951,"depth":107,"text":30952,"children":31015},[31016,31017],{"id":30958,"depth":294,"text":30959},{"id":30969,"depth":294,"text":30970},"2023-03-29","IBM Cloud has recently announced that they will no longer be providing their Cloud App Service Starter Kits, including the Node-RED Starter Application. Don't worry though, FlowFuse has you covered.","\u002Fimages\u002Fblog\u002Ftile-ibm-cloud-starter-removed--text.png",{"excerpt":31022},{"type":12,"value":31023},[31024],[15,31025,30839,31026,30845,31029,29],{},[63,31027,30844],{"href":30842,"rel":31028},[67],[63,31030,30850],{"href":30848,"rel":31031},[67],"\u002Fblog\u002F2023\u002F03\u002Fibmcloud-starter-removed",{"title":30833,"description":31019},{"loc":31032},"blog\u002F2023\u002F03\u002Fibmcloud-starter-removed","Get up and running with a Starter Application for Node-RED with FlowFuse or migrate your existing flows from IBM Cloud",[8650,138,136],"CGxqDQQR7gZpNsBn6HyDRRkQWcEVXvEP89_MPj1D0jo",{"id":31040,"title":29151,"authors":31041,"body":31042,"cta":3,"date":31216,"description":31217,"extension":116,"image":29183,"lastUpdated":6064,"meta":31218,"navigation":129,"path":31223,"seo":31224,"sitemap":31225,"stem":31226,"subtitle":30352,"tags":31227,"tldr":3,"video":3,"__hash__":31228},"blog\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-5.md",[19494],{"type":12,"value":31043,"toc":31210},[31044,31046,31050,31053,31056,31067,31070,31084,31090,31093,31097,31100,31103,31120,31123,31129,31132,31136,31139,31153,31159,31162,31166,31169,31187,31191,31196,31204],[15,31045,28944],{},[399,31047,31049],{"id":31048},"_1-copy-and-share-your-flows-using-export-and-import","1. Copy and share your flows using Export and Import",[15,31051,31052],{},"Node-RED provides both import and export features that allow you to save your flows and settings as compressed JSON files. You can use these features to easily move your flows between multiple Node-RED instances or to back up your work.",[15,31054,31055],{},"To import a flow, follow these steps:",[271,31057,31058,31061,31064],{},[42,31059,31060],{},"Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on \"Import\".",[42,31062,31063],{},"Select the JSON file that contains the flow you want to import.",[42,31065,31066],{},"Click on \"Import\" and the flow will be imported into the current instance of Node-RED.",[15,31068,31069],{},"To export a flow, follow these steps:",[271,31071,31072,31075,31078,31081],{},[42,31073,31074],{},"Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on \"Export\".",[42,31076,31077],{},"Select the type of export you want to perform - this can be either the \"Clipboard\" or \"File\".",[42,31079,31080],{},"If you chose \"File\" you will be prompted to save a compressed JSON file to your computer; if you chose \"Clipboard\" the flow JSON will be copied to your clipboard.",[42,31082,31083],{},"Use the exported file or clipboard content to import into a different instance of Node-RED.",[15,31085,31086],{},[165,31087],{"alt":31088,"src":31089,"title":31088},"Importing and exporting your flows","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fimport-export.gif",[15,31091,31092],{},"Keep in mind that some nodes or flows may require additional setup or node installation on the Node-RED instance you import your flow to.",[399,31094,31096],{"id":31095},"_2-import-helpful-example-flows-provided-with-custom-nodes","2. Import helpful example flows provided with custom nodes",[15,31098,31099],{},"In Node-RED, custom nodes can include examples that provide users with a starting point for using the node. These examples can help users understand how a node works and how it can be integrated into their flows.",[15,31101,31102],{},"To use example flows in custom nodes, follow these steps:",[271,31104,31105,31108,31111,31114,31117],{},[42,31106,31107],{},"Open the Node-RED editor and drag the custom node you want to use into your flow.",[42,31109,31110],{},"Double-click on the node to open its configuration panel.",[42,31112,31113],{},"Look for an \"Examples\" menu or button within the node configuration panel. The name and location of the Examples button can vary depending on the node.",[42,31115,31116],{},"Click on the \"Examples\" button to bring up a list of example flows included with the node.",[42,31118,31119],{},"Select the example flow you want to use and click on \"Import\" to add the example flow to your Node-RED workspace.",[15,31121,31122],{},"Once the example flow has been added to your workspace, you can modify it to fit your specific needs.",[15,31124,31125],{},[165,31126],{"alt":31127,"src":31128,"title":31127},"Using the example flow included in the moment node","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fexample.gif",[15,31130,31131],{},"It's important to note that while custom node examples can be a useful starting point, they may not always work seamlessly with your other flows or nodes. Be sure to thoroughly test any custom node examples before incorporating them into a production environment.",[399,31133,31135],{"id":31134},"_3-group-nodes-together-to-make-your-flows-easier-to-read","3. Group nodes together to make your flows easier to read",[15,31137,31138],{},"The group feature in Node-RED allows users to visually group nodes together within the workspace. This feature offers several benefits:",[271,31140,31141,31144,31147,31150],{},[42,31142,31143],{},"Improved organization: The group feature allows you to group related nodes visually, which can make your flow easier to understand and navigate. This can be particularly helpful for larger, more complex flows.",[42,31145,31146],{},"Simplified editing: When you group nodes together, you can edit or move them as a single unit, rather than individually. This can save time and reduce the chance of errors.",[42,31148,31149],{},"Easier sharing: When you share your flow with others, the group feature allows you to package related nodes together, making it easier for others to understand and use your flow.",[42,31151,31152],{},"Reduced clutter: Grouping nodes can help reduce the visual clutter in your workspace, making it easier to focus on key aspects of your flow.",[15,31154,31155],{},[165,31156],{"alt":31157,"src":31158,"title":31157},"Grouping your nodes to make them easier to read","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fgroups.gif",[15,31160,31161],{},"Overall, the group feature in Node-RED is a valuable tool that can help users better organise, edit, and share their flows.",[15,31163,29135,31164,29],{},[63,31165,12885],{"href":29138},[15,31167,31168],{},"You can read our previous Node-RED tips here.",[15,31170,31171,31174,31176,31178,31180,31182,31184],{},[63,31172,31173],{"href":29156},"Node-RED Tips - Smooth, Catch, and Math",[5368,31175],{},[63,31177,29163],{"href":29162},[5368,31179],{},[63,31181,29169],{"href":29168},[5368,31183],{},[63,31185,31186],{"href":29174},"Node-RED Tips - Wiring Shortcuts\n",[31,31188,31190],{"id":31189},"version-control-and-collaboration-for-your-node-red-flows","Version Control and Collaboration for Your Node-RED Flows",[15,31192,31193,31195],{},[63,31194,803],{"href":810}," simplifies managing and collaborating on your Node-RED flows with seamless version control. You can easily track changes, take snapshots, and revert to previous versions, ensuring your work is always safe and recoverable.",[15,31197,31198,31199,31203],{},"With FlowFuse ",[63,31200,31202],{"href":31201},"\u002Fdocs\u002Fuser\u002Fshared-library\u002F#shared-team-library","Team Library",", sharing flows across different Node-RED instances is effortless. This Library feature allows you to organize and share flows among team members without the need for manual copying, making collaboration more efficient and effective.",[15,31205,31206,31209],{},[19,31207,31208],{},"[Sign up]({% include \"sign-up-url.njk\" %}) for a free trial today and discover how FlowFuse can enhance your Node","*",{"title":103,"searchDepth":104,"depth":104,"links":31211},[31212,31213,31214,31215],{"id":31048,"depth":294,"text":31049},{"id":31095,"depth":294,"text":31096},{"id":31134,"depth":294,"text":31135},{"id":31189,"depth":107,"text":31190},"2023-03-27","Learn how to save time on Node-RED with three essential techniques: exporting and importing flows, accessing example flows from custom nodes, and organizing nodes using groups for improved clarity and management.",{"excerpt":31219},{"type":12,"value":31220},[31221],[15,31222,28944],{},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-5",{"title":29151,"description":31217},{"loc":31223},"blog\u002F2023\u002F03\u002F3-quick-node-red-tips-5",[8650,138,7457,11298],"pWB70rderYipovtfUJmLU0l97JsyyMAfLCu4CbgYAQg",{"id":31230,"title":31231,"authors":31232,"body":31233,"cta":3,"date":31360,"description":31361,"extension":116,"image":31362,"lastUpdated":6064,"meta":31363,"navigation":129,"path":31368,"seo":31369,"sitemap":31370,"stem":31371,"subtitle":31372,"tags":31373,"tldr":3,"video":3,"__hash__":31374},"blog\u002Fblog\u002F2023\u002F03\u002Fwhy-should-you-use-node-red-function-nodes.md","The benefits and drawbacks of using Node-RED function nodes",[19494],{"type":12,"value":31234,"toc":31354},[31235,31238,31242,31248,31280,31284,31290,31322,31326,31329,31336,31342,31345,31348,31351],[15,31236,31237],{},"Function nodes are an essential part of Node-RED. They allow you to write custom JavaScript functions that can be used in your Node-RED flows. In this blog post, I will discuss some of the benefits and drawbacks of using Function nodes in your next project.",[31,31239,31241],{"id":31240},"_5-benefits-of-using-function-nodes","5 Benefits of using Function Nodes:",[15,31243,31244],{},[165,31245],{"alt":31246,"src":31247,"title":31246},"Example showing how to use the function node","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Ffunction-example.gif",[271,31249,31250,31256,31262,31268,31274],{},[42,31251,31252,31255],{},[19,31253,31254],{},"Customisation:"," Function nodes allow you to write custom JavaScript functions that can be tailored to your specific needs. You can create complex functions that perform a variety of tasks, the only limit is your programming skills.",[42,31257,31258,31261],{},[19,31259,31260],{},"Reusability:"," Function nodes can be reused in multiple flows, saving you time and effort. You can create a library of custom functions that can be easily accessed and reused in different flows.",[42,31263,31264,31267],{},[19,31265,31266],{},"Debugging:"," Function nodes provide an easy way to debug your code. You can use console.log statements to output debug information to the Node-RED debug panel, making it easier to identify and fix issues.",[42,31269,31270,31273],{},[19,31271,31272],{},"Performance:"," Function nodes can be more performant than using multiple nodes to achieve the same result. By combining multiple tasks into a single function, you can improve performance, assuming your code is efficient.",[42,31275,31276,31279],{},[19,31277,31278],{},"Flexibility:"," Function nodes provide a high degree of flexibility. You can use them to perform tasks that are not possible using a single, standard Node-RED node, such as complex data manipulation.",[31,31281,31283],{"id":31282},"_5-benefits-of-avoiding-function-nodes","5 Benefits of avoiding Function Nodes:",[15,31285,31286],{},[165,31287],{"alt":31288,"src":31289,"title":31288},"Example showing how to not use the function node","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fno-function-example.gif",[271,31291,31292,31298,31304,31310,31316],{},[42,31293,31294,31297],{},[19,31295,31296],{},"Simplicity:"," Not using function nodes can make your flows simpler and easier to understand. By using standard Node-RED nodes, you can create flows that are easy to follow and maintain for both you and your team.",[42,31299,31300,31303],{},[19,31301,31302],{},"Ease of Use:"," Standard Node-RED nodes are easy to use and require no programming knowledge. This makes it easier for non-technical users to create and maintain flows.",[42,31305,31306,31309],{},[19,31307,31308],{},"Modularity:"," By using standard Node-RED nodes, you can create modular flows that can be easily modified and extended. This makes it easier to add new functionality to your flows as your needs change.",[42,31311,31312,31315],{},[19,31313,31314],{},"Community Support:"," Standard Node-RED nodes have a large and active community, providing support and resources for users. This can make it easier to find solutions to common problems and share knowledge with others.",[42,31317,31318,31321],{},[19,31319,31320],{},"Compatibility:"," Standard Node-RED nodes are usually compatible with all versions of Node-RED, making it easier to migrate flows between different environments.",[31,31323,31325],{"id":31324},"how-to-easily-create-function-nodes-in-flowfuse","How to Easily Create Function Nodes in FlowFuse",[15,31327,31328],{},"FlowFuse offers a robust platform for building, scaling, and securing your Node-RED applications.",[15,31330,31331,31332,31335],{},"We are constantly adding new features to make it easy to use in the enterprise where you can rapidly improve your industrial processes. The ",[19,31333,31334],{},"\"FlowFuse Assistant.\""," for example is an AI-powered tool that simplifies the creation of Function nodes. You only need to provide a prompt, and the assistant generates the Function nodes for you.",[15,31337,31338,31339,29],{},"For more details on using the FlowFuse Assistant, visit ",[63,31340,31341],{"href":30128},"the Assistants Documentation",[31,31343,31344],{"id":1424},"Conclusion:",[15,31346,31347],{},"Function nodes are particularly valuable for users who possess JavaScript programming skills. They allow for complex tasks, advanced data manipulation, and integration with external APIs, providing a high level of customization and flexibility. However, they require a good understanding of JavaScript to implement effectively and can be more challenging to manage and debug compared to standard Node-RED nodes.",[15,31349,31350],{},"On the other hand, standard Node-RED nodes offer a simpler and more accessible approach, making it easy for users without programming expertise to create and maintain flows. They are designed for straightforward tasks and provide modularity, benefiting from a supportive community for troubleshooting and knowledge sharing.",[15,31352,31353],{},"Ultimately, the choice between using function nodes and standard nodes will depend on your project's requirements and your familiarity with JavaScript. If you seek deep customization and flexibility, function nodes, enhanced by tools like FlowFuse Assistant, might be the best choice. For those who value simplicity and ease of use, standard Node-RED nodes are a great fit.",{"title":103,"searchDepth":104,"depth":104,"links":31355},[31356,31357,31358,31359],{"id":31240,"depth":107,"text":31241},{"id":31282,"depth":107,"text":31283},{"id":31324,"depth":107,"text":31325},{"id":1424,"depth":107,"text":31344},"2023-03-20","Explore the benefits and drawbacks of Function nodes in Node-RED projects, balancing customizability with simplicity for optimal flow design.","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Ffunction-nodes.png",{"excerpt":31364},{"type":12,"value":31365},[31366],[15,31367,31237],{},"\u002Fblog\u002F2023\u002F03\u002Fwhy-should-you-use-node-red-function-nodes",{"title":31231,"description":31361},{"loc":31368},"blog\u002F2023\u002F03\u002Fwhy-should-you-use-node-red-function-nodes","In this blog post, I will discuss some of the benefits and drawbacks of using Function nodes in your next Node-RED project.",[8650,138,7457],"sELa27z-t_WQNyIFR8gEqzXN8aYk4pWj5Nm--rFjQZQ",{"id":31376,"title":31173,"authors":31377,"body":31378,"cta":3,"date":36225,"description":30352,"extension":116,"image":29183,"lastUpdated":6064,"meta":36226,"navigation":129,"path":36231,"seo":36232,"sitemap":36233,"stem":36234,"subtitle":30352,"tags":36235,"tldr":3,"video":3,"__hash__":36237},"blog\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-4.md",[19494],{"type":12,"value":31379,"toc":36219},[31380,31382,31386,31389,31395,31398,36113,36117,36124,36130,36133,36154,36158,36161,36164,36170,36173,36192,36196,36200,36207,36210,36216],[15,31381,28944],{},[399,31383,31385],{"id":31384},"_1-use-the-smooth-node-to-get-the-minimum-and-maximum-values-of-your-payloads","1. Use the Smooth node to get the minimum and maximum values of your payloads",[15,31387,31388],{},"When taking data in from sensors sometimes a spurious value can be sent into your flow. This can result in oddities in a graph or even misfiring of actions such as turning on a heating system. The Smooth custom node allows you to store the min and max of a payload for the last few messages received.",[15,31390,31391],{},[165,31392],{"alt":31393,"src":31394,"title":31393},"Using the Smooth node to return highest value from the last 100 payloads","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fsmooth.png",[15,31396,31397],{},"You can in turn use this to ignore values that deviate too far from the sample. To help demonstrate the Smooth node, I've created a flow you can import into Node-RED.",[1756,31399,31401],{"className":5097,"code":31400,"language":5099,"meta":103,"style":103},"[{\"id\":\"9484c25a0120bd48\",\"type\":\"group\",\"z\":\"dd95c0bca1101c86\",\"name\":\"Automatically outputs random value (temperature in Celcius) between 0 & 25 every second\",\"style\":{\"label\":!0},\"nodes\":[\"e4f972f9daad6246\",\"c7fc075a1915e87b\",\"966a772c46dc2888\"],\"x\":34,\"y\":59,\"w\":574,\"h\":82},{\"id\":\"e4f972f9daad6246\",\"type\":\"link out\",\"z\":\"dd95c0bca1101c86\",\"g\":\"9484c25a0120bd48\",\"name\":\"link out 1\",\"mode\":\"link\",\"links\":[\"33594c64783cdc45\",\"e6bf7b494b48861e\"],\"x\":355,\"y\":100,\"wires\":[]},{\"id\":\"c7fc075a1915e87b\",\"type\":\"inject\",\"z\":\"dd95c0bca1101c86\",\"g\":\"9484c25a0120bd48\",\"name\":\"\",\"props\":[],\"repeat\":\"1\",\"crontab\":\"\",\"once\":!1,\"onceDelay\":0.1,\"topic\":\"\",\"x\":130,\"y\":100,\"wires\":[[\"966a772c46dc2888\"]]},{\"id\":\"966a772c46dc2888\",\"type\":\"random\",\"z\":\"dd95c0bca1101c86\",\"g\":\"9484c25a0120bd48\",\"name\":\"\",\"low\":\"0\",\"high\":\"25\",\"inte\":\"true\",\"property\":\"payload\",\"x\":260,\"y\":100,\"wires\":[[\"e4f972f9daad6246\"]]},{\"id\":\"37380f26e8bfc98a\",\"type\":\"group\",\"z\":\"dd95c0bca1101c86\",\"name\":\"Calculate average, high and low, save to flow\",\"style\":{\"label\":!0},\"nodes\":[\"cc3978c7c4ea56ed\",\"e1819526a5f365c8\",\"33594c64783cdc45\",\"fea261a15b3b7683\",\"e28a69232f1cac53\",\"aecb1727be523240\",\"e30039ee13e480a8\"],\"x\":34,\"y\":259,\"w\":552,\"h\":142},{\"id\":\"cc3978c7c4ea56ed\",\"type\":\"change\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"high\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":!1,\"x\":310,\"y\":300,\"wires\":[[\"aecb1727be523240\"]]},{\"id\":\"e1819526a5f365c8\",\"type\":\"change\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"low\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":!1,\"x\":310,\"y\":360,\"wires\":[[\"e30039ee13e480a8\"]]},{\"id\":\"33594c64783cdc45\",\"type\":\"link in\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"link in 1\",\"links\":[\"c07b2e101cecbd3b\",\"e4f972f9daad6246\"],\"x\":75,\"y\":320,\"wires\":[[\"e28a69232f1cac53\",\"fea261a15b3b7683\"]]},{\"id\":\"fea261a15b3b7683\",\"type\":\"smooth\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"Min\",\"property\":\"payload\",\"action\":\"min\",\"count\":\"100\",\"round\":\"2\",\"mult\":\"single\",\"reduce\":!1,\"x\":170,\"y\":360,\"wires\":[[\"e1819526a5f365c8\"]]},{\"id\":\"e28a69232f1cac53\",\"type\":\"smooth\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"Max\",\"property\":\"payload\",\"action\":\"max\",\"count\":\"100\",\"round\":\"2\",\"mult\":\"single\",\"reduce\":!1,\"x\":170,\"y\":300,\"wires\":[[\"cc3978c7c4ea56ed\"]]},{\"id\":\"aecb1727be523240\",\"type\":\"debug\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"debug 23\",\"active\":!0,\"tosidebar\":!1,\"console\":!1,\"tostatus\":!0,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"payload\",\"statusType\":\"auto\",\"x\":480,\"y\":300,\"wires\":[]},{\"id\":\"e30039ee13e480a8\",\"type\":\"debug\",\"z\":\"dd95c0bca1101c86\",\"g\":\"37380f26e8bfc98a\",\"name\":\"debug 25\",\"active\":!0,\"tosidebar\":!1,\"console\":!1,\"tostatus\":!0,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"payload\",\"statusType\":\"auto\",\"x\":480,\"y\":360,\"wires\":[]},{\"id\":\"ec11a9ee9148b0b5\",\"type\":\"group\",\"z\":\"dd95c0bca1101c86\",\"name\":\"Evaluate if an incoming value is between flow.high and flow.low, if it is not, send the message down a different wire and show an alert in debug\",\"style\":{\"label\":!0},\"nodes\":[\"9393c22b2e3c1ec8\",\"1cd18307cb919159\",\"7c1f474e646765a7\",\"a209cd10f33ec401\",\"f4b15283e55babf4\",\"e6bf7b494b48861e\"],\"x\":34,\"y\":419,\"w\":1032,\"h\":162},{\"id\":\"9393c22b2e3c1ec8\",\"type\":\"switch\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"Was the value between flow.high and flow.low?\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"btwn\",\"v\":\"high\",\"vt\":\"flow\",\"v2\":\"low\",\"v2t\":\"flow\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":!1,\"outputs\":2,\"x\":300,\"y\":480,\"wires\":[[\"1cd18307cb919159\"],[\"7c1f474e646765a7\",\"a209cd10f33ec401\"]]},{\"id\":\"1cd18307cb919159\",\"type\":\"debug\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"debug 15\",\"active\":!0,\"tosidebar\":!0,\"console\":!1,\"tostatus\":!1,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":560,\"y\":460,\"wires\":[]},{\"id\":\"7c1f474e646765a7\",\"type\":\"debug\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"debug 16\",\"active\":!1,\"tosidebar\":!0,\"console\":!1,\"tostatus\":!1,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":560,\"y\":540,\"wires\":[]},{\"id\":\"a209cd10f33ec401\",\"type\":\"change\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"Alert to debug when value is outside of the range\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"The value was outside of the range\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":!1,\"x\":690,\"y\":500,\"wires\":[[\"f4b15283e55babf4\"]]},{\"id\":\"f4b15283e55babf4\",\"type\":\"debug\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"debug 17\",\"active\":!0,\"tosidebar\":!0,\"console\":!1,\"tostatus\":!1,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":960,\"y\":500,\"wires\":[]},{\"id\":\"e6bf7b494b48861e\",\"type\":\"link in\",\"z\":\"dd95c0bca1101c86\",\"g\":\"ec11a9ee9148b0b5\",\"name\":\"link in 2\",\"links\":[\"e4f972f9daad6246\",\"c07b2e101cecbd3b\"],\"x\":75,\"y\":480,\"wires\":[[\"9393c22b2e3c1ec8\"]]},{\"id\":\"caf4214602d5f2c9\",\"type\":\"group\",\"z\":\"dd95c0bca1101c86\",\"name\":\"Manually send a spurious value\",\"style\":{\"label\":!0},\"nodes\":[\"14097fb7ba3a9ecc\",\"c07b2e101cecbd3b\"],\"x\":34,\"y\":159,\"w\":232,\"h\":82},{\"id\":\"14097fb7ba3a9ecc\",\"type\":\"inject\",\"z\":\"dd95c0bca1101c86\",\"g\":\"caf4214602d5f2c9\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":!1,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"75\",\"payloadType\":\"num\",\"x\":130,\"y\":200,\"wires\":[[\"c07b2e101cecbd3b\"]]},{\"id\":\"c07b2e101cecbd3b\",\"type\":\"link out\",\"z\":\"dd95c0bca1101c86\",\"g\":\"caf4214602d5f2c9\",\"name\":\"link out 2\",\"mode\":\"link\",\"links\":[\"33594c64783cdc45\",\"e6bf7b494b48861e\"],\"x\":225,\"y\":200,\"wires\":[]}]\n",[924,31402,31403],{"__ignoreMap":103},[1107,31404,31405,31408,31410,31412,31414,31416,31418,31421,31423,31425,31427,31430,31432,31434,31436,31439,31441,31443,31445,31448,31450,31452,31454,31457,31459,31461,31463,31465,31467,31469,31471,31474,31476,31478,31480,31482,31484,31487,31489,31492,31494,31496,31499,31501,31504,31506,31509,31511,31514,31516,31519,31521,31523,31525,31528,31530,31532,31534,31537,31539,31542,31544,31547,31549,31551,31554,31556,31558,31561,31563,31565,31568,31570,31572,31575,31577,31579,31582,31584,31586,31589,31591,31593,31596,31599,31601,31603,31605,31607,31609,31611,31613,31615,31617,31619,31621,31623,31625,31628,31630,31632,31634,31636,31638,31640,31642,31644,31646,31648,31650,31653,31655,31657,31659,31661,31663,31665,31667,31669,31671,31673,31675,31678,31680,31682,31684,31687,31689,31691,31693,31696,31698,31700,31702,31705,31707,31709,31711,31714,31716,31718,31720,31723,31725,31727,31729,31731,31733,31735,31738,31740,31742,31744,31746,31748,31751,31753,31755,31758,31760,31763,31765,31767,31769,31771,31773,31775,31777,31779,31781,31783,31785,31787,31789,31791,31793,31795,31797,31799,31801,31803,31805,31807,31809,31811,31813,31815,31817,31819,31821,31823,31825,31827,31829,31831,31833,31835,31837,31839,31841,31844,31846,31849,31851,31854,31856,31858,31860,31862,31864,31866,31868,31871,31873,31875,31877,31879,31881,31884,31886,31888,31890,31892,31894,31896,31899,31901,31903,31906,31908,31910,31913,31915,31917,31919,31921,31923,31925,31927,31929,31932,31934,31936,31938,31940,31942,31944,31946,31948,31950,31952,31955,31957,31959,31961,31964,31966,31968,31970,31972,31974,31976,31978,31980,31982,31984,31986,31988,31990,31992,31994,31996,31998,32000,32002,32004,32006,32008,32010,32012,32014,32016,32018,32020,32022,32024,32026,32028,32030,32032,32034,32036,32038,32040,32042,32045,32047,32049,32051,32053,32055,32057,32059,32062,32064,32066,32068,32071,32073,32075,32077,32080,32082,32084,32086,32088,32090,32092,32094,32097,32099,32101,32103,32105,32107,32109,32111,32113,32115,32117,32120,32122,32124,32126,32128,32130,32132,32134,32136,32138,32140,32142,32144,32146,32148,32150,32152,32154,32156,32158,32160,32163,32165,32167,32169,32171,32173,32175,32177,32179,32181,32183,32185,32187,32189,32191,32193,32195,32197,32199,32201,32203,32205,32207,32209,32212,32214,32216,32218,32220,32222,32224,32226,32228,32230,32232,32234,32236,32238,32240,32242,32244,32246,32248,32251,32253,32255,32257,32260,32262,32264,32266,32268,32270,32272,32274,32277,32279,32281,32283,32286,32288,32290,32292,32295,32297,32299,32301,32304,32306,32308,32310,32312,32314,32316,32318,32320,32322,32324,32326,32328,32331,32333,32335,32337,32339,32341,32344,32346,32348,32350,32352,32354,32357,32359,32361,32363,32365,32367,32369,32371,32373,32375,32377,32379,32381,32383,32385,32387,32389,32391,32393,32395,32397,32399,32401,32403,32405,32407,32409,32411,32413,32415,32417,32419,32421,32423,32425,32427,32429,32431,32433,32435,32437,32440,32442,32445,32447,32450,32452,32454,32456,32458,32460,32462,32464,32466,32468,32470,32472,32474,32476,32478,32480,32483,32485,32487,32489,32491,32493,32495,32497,32500,32502,32504,32506,32508,32510,32512,32514,32517,32519,32521,32523,32525,32527,32530,32532,32535,32537,32539,32541,32543,32545,32547,32549,32551,32553,32555,32557,32559,32561,32563,32565,32567,32569,32571,32573,32575,32577,32579,32581,32584,32586,32588,32590,32592,32594,32596,32598,32600,32602,32605,32607,32609,32611,32613,32615,32618,32620,32622,32624,32626,32628,32630,32632,32634,32636,32638,32640,32642,32644,32646,32648,32650,32652,32654,32656,32658,32660,32662,32664,32666,32668,32670,32672,32674,32676,32678,32680,32682,32684,32686,32688,32690,32692,32694,32696,32698,32700,32702,32704,32706,32708,32710,32712,32714,32716,32718,32720,32722,32724,32726,32728,32730,32732,32734,32736,32738,32740,32742,32744,32746,32748,32750,32752,32754,32756,32758,32760,32762,32764,32766,32768,32770,32772,32774,32776,32778,32780,32782,32784,32786,32788,32790,32792,32794,32796,32798,32800,32802,32804,32806,32808,32810,32812,32814,32816,32818,32820,32822,32824,32826,32828,32830,32832,32834,32836,32838,32840,32842,32844,32846,32848,32850,32852,32854,32856,32858,32860,32862,32864,32866,32868,32870,32872,32874,32876,32878,32880,32882,32884,32886,32888,32890,32892,32894,32896,32898,32900,32902,32904,32906,32908,32910,32912,32914,32916,32918,32920,32922,32924,32926,32928,32931,32933,32935,32937,32939,32941,32943,32945,32947,32949,32951,32953,32955,32957,32959,32961,32963,32965,32967,32969,32971,32973,32975,32977,32980,32982,32984,32986,32988,32990,32992,32994,32997,32999,33001,33003,33005,33007,33009,33011,33013,33015,33017,33020,33022,33024,33026,33028,33030,33033,33035,33037,33039,33041,33043,33045,33047,33049,33051,33053,33055,33057,33059,33061,33063,33065,33067,33069,33071,33073,33075,33077,33079,33081,33083,33085,33088,33090,33092,33094,33096,33098,33100,33102,33104,33106,33108,33110,33112,33114,33116,33118,33120,33122,33124,33126,33128,33130,33132,33134,33136,33138,33140,33142,33144,33146,33148,33150,33152,33154,33156,33158,33160,33162,33164,33166,33169,33171,33173,33175,33178,33180,33182,33184,33186,33188,33190,33192,33194,33196,33198,33200,33202,33204,33206,33208,33211,33213,33215,33217,33220,33222,33224,33226,33229,33231,33233,33235,33237,33239,33241,33243,33245,33247,33250,33252,33254,33256,33258,33260,33262,33264,33266,33268,33270,33272,33274,33276,33278,33280,33282,33284,33286,33288,33290,33292,33294,33296,33298,33300,33302,33304,33306,33308,33310,33312,33314,33316,33318,33320,33322,33324,33326,33328,33330,33332,33334,33336,33338,33340,33342,33344,33346,33348,33350,33352,33354,33356,33358,33360,33362,33364,33366,33368,33370,33372,33374,33376,33378,33380,33382,33384,33386,33389,33391,33393,33395,33397,33399,33401,33403,33405,33407,33409,33411,33413,33415,33417,33419,33421,33423,33425,33427,33429,33431,33433,33435,33437,33439,33441,33443,33445,33447,33449,33451,33453,33455,33457,33459,33461,33463,33465,33467,33469,33471,33473,33475,33477,33479,33481,33483,33485,33487,33489,33491,33493,33495,33497,33499,33501,33503,33505,33507,33509,33511,33513,33515,33517,33519,33521,33523,33525,33527,33529,33531,33533,33535,33537,33539,33541,33543,33545,33547,33549,33551,33553,33555,33557,33559,33561,33563,33565,33567,33569,33572,33574,33576,33578,33580,33582,33584,33586,33588,33590,33592,33595,33597,33599,33601,33603,33605,33607,33610,33612,33614,33616,33618,33620,33622,33625,33627,33629,33631,33633,33635,33637,33640,33642,33644,33646,33648,33650,33652,33654,33657,33659,33661,33663,33665,33667,33669,33671,33674,33676,33678,33680,33682,33684,33686,33688,33691,33693,33695,33697,33700,33702,33704,33706,33708,33710,33712,33715,33717,33719,33721,33723,33725,33727,33729,33731,33733,33735,33737,33739,33741,33743,33745,33747,33749,33751,33753,33755,33757,33759,33761,33763,33765,33767,33769,33771,33773,33775,33777,33779,33781,33783,33785,33787,33789,33791,33793,33795,33797,33799,33801,33803,33805,33807,33809,33811,33814,33816,33818,33820,33822,33824,33826,33828,33830,33832,33834,33836,33838,33840,33842,33844,33846,33848,33850,33852,33854,33856,33858,33860,33862,33864,33866,33868,33870,33872,33874,33876,33878,33880,33882,33884,33886,33888,33890,33892,33894,33896,33898,33900,33902,33904,33906,33908,33910,33912,33914,33916,33918,33920,33922,33924,33926,33928,33930,33932,33934,33936,33938,33940,33942,33944,33946,33948,33950,33952,33954,33956,33958,33960,33962,33964,33966,33968,33970,33972,33974,33976,33978,33980,33983,33985,33987,33989,33991,33993,33995,33997,33999,34001,34003,34005,34007,34009,34011,34013,34015,34017,34019,34021,34023,34025,34027,34029,34032,34034,34036,34038,34040,34042,34044,34046,34048,34050,34052,34054,34056,34058,34060,34062,34064,34066,34068,34071,34073,34075,34077,34080,34082,34084,34086,34089,34091,34093,34095,34098,34100,34102,34104,34107,34109,34111,34113,34115,34117,34119,34121,34123,34125,34127,34129,34131,34133,34135,34137,34139,34142,34144,34146,34148,34150,34152,34155,34157,34159,34161,34163,34165,34168,34170,34172,34174,34176,34178,34180,34182,34184,34186,34188,34190,34192,34194,34196,34198,34200,34202,34204,34206,34208,34210,34212,34214,34216,34218,34220,34222,34224,34226,34228,34230,34232,34234,34236,34238,34240,34242,34244,34247,34249,34251,34253,34255,34257,34259,34261,34263,34265,34267,34269,34272,34274,34276,34278,34280,34282,34284,34286,34288,34290,34292,34294,34296,34298,34300,34302,34305,34307,34309,34311,34314,34316,34318,34320,34322,34324,34326,34328,34331,34333,34335,34337,34339,34341,34343,34345,34348,34350,34352,34354,34356,34358,34360,34362,34365,34367,34369,34371,34373,34375,34377,34379,34381,34383,34385,34387,34390,34392,34394,34396,34399,34401,34403,34405,34407,34409,34411,34413,34416,34418,34420,34422,34424,34426,34428,34430,34432,34434,34436,34438,34440,34442,34444,34446,34448,34450,34452,34454,34456,34458,34460,34462,34464,34466,34468,34470,34472,34474,34476,34479,34481,34483,34485,34487,34489,34491,34493,34495,34497,34499,34501,34503,34505,34507,34509,34511,34513,34515,34517,34519,34521,34523,34525,34527,34529,34531,34533,34535,34537,34539,34541,34543,34545,34547,34549,34551,34553,34555,34557,34559,34561,34563,34565,34567,34569,34572,34574,34576,34578,34580,34582,34584,34586,34588,34590,34592,34594,34596,34598,34600,34602,34604,34606,34608,34610,34612,34614,34616,34618,34620,34622,34624,34626,34628,34630,34632,34634,34636,34638,34640,34642,34644,34646,34648,34650,34652,34654,34656,34658,34660,34662,34664,34666,34668,34670,34672,34674,34676,34678,34680,34682,34684,34687,34689,34691,34693,34695,34697,34700,34702,34704,34706,34708,34710,34712,34714,34716,34718,34720,34722,34724,34726,34728,34730,34732,34734,34736,34738,34740,34742,34744,34746,34748,34750,34752,34754,34756,34758,34760,34762,34764,34766,34768,34770,34772,34774,34776,34778,34780,34782,34784,34787,34789,34791,34793,34795,34797,34799,34801,34803,34805,34807,34809,34811,34813,34815,34817,34819,34821,34823,34825,34827,34829,34831,34833,34835,34837,34839,34841,34843,34845,34847,34849,34851,34853,34855,34857,34859,34861,34863,34865,34867,34869,34871,34873,34875,34877,34879,34881,34883,34885,34887,34889,34891,34893,34895,34897,34899,34901,34903,34905,34907,34909,34911,34914,34916,34918,34920,34922,34924,34926,34928,34930,34932,34934,34936,34938,34940,34942,34944,34946,34948,34950,34952,34954,34956,34958,34960,34962,34964,34966,34968,34970,34972,34974,34976,34978,34980,34982,34984,34986,34988,34990,34992,34994,34996,34998,35001,35003,35005,35007,35009,35011,35013,35015,35017,35019,35021,35023,35025,35027,35029,35031,35033,35035,35037,35039,35041,35043,35045,35047,35049,35051,35053,35055,35057,35059,35061,35063,35065,35067,35069,35071,35074,35076,35078,35080,35082,35084,35086,35088,35091,35093,35095,35097,35099,35101,35103,35105,35107,35109,35111,35113,35115,35117,35119,35121,35123,35125,35127,35129,35131,35133,35135,35137,35139,35141,35143,35145,35147,35149,35151,35153,35155,35157,35159,35161,35163,35165,35168,35170,35172,35174,35176,35178,35181,35183,35185,35187,35189,35191,35193,35195,35197,35199,35201,35203,35205,35207,35209,35211,35213,35215,35217,35219,35221,35223,35225,35227,35229,35231,35233,35235,35237,35239,35241,35243,35245,35247,35249,35251,35253,35255,35257,35259,35261,35263,35265,35267,35269,35271,35273,35276,35278,35280,35282,35284,35286,35288,35290,35292,35294,35296,35298,35300,35302,35304,35306,35308,35310,35312,35314,35316,35318,35320,35322,35324,35326,35328,35330,35332,35334,35336,35338,35340,35342,35344,35346,35348,35350,35352,35354,35356,35358,35360,35362,35364,35366,35368,35370,35372,35374,35376,35378,35380,35382,35384,35386,35388,35391,35393,35395,35397,35399,35401,35403,35405,35407,35409,35411,35413,35415,35417,35419,35421,35423,35425,35427,35429,35431,35433,35435,35437,35439,35441,35443,35445,35447,35449,35451,35453,35455,35457,35459,35461,35463,35465,35467,35469,35471,35473,35475,35477,35479,35481,35483,35485,35487,35490,35492,35494,35496,35498,35500,35502,35504,35506,35508,35510,35512,35514,35516,35518,35520,35522,35524,35526,35528,35530,35532,35534,35536,35538,35540,35542,35544,35546,35548,35550,35552,35554,35556,35558,35560,35562,35564,35566,35568,35571,35573,35575,35577,35579,35581,35583,35585,35587,35589,35591,35593,35595,35597,35599,35601,35603,35605,35607,35609,35611,35613,35615,35617,35620,35622,35624,35626,35628,35630,35632,35634,35636,35638,35640,35642,35644,35646,35648,35650,35652,35654,35656,35659,35661,35663,35665,35667,35669,35671,35673,35675,35677,35679,35681,35683,35685,35687,35689,35691,35694,35696,35698,35700,35702,35704,35707,35709,35711,35713,35715,35717,35719,35721,35723,35725,35727,35729,35731,35733,35735,35737,35739,35741,35743,35745,35747,35749,35751,35753,35755,35757,35759,35761,35763,35765,35767,35769,35771,35773,35775,35777,35779,35781,35783,35785,35787,35789,35791,35793,35795,35797,35799,35801,35803,35805,35807,35809,35811,35813,35815,35817,35819,35821,35823,35825,35827,35829,35831,35833,35835,35837,35839,35841,35843,35845,35847,35849,35851,35853,35855,35857,35859,35861,35863,35865,35867,35869,35871,35873,35875,35877,35879,35881,35883,35885,35887,35889,35891,35893,35895,35897,35899,35901,35904,35906,35908,35910,35913,35915,35917,35919,35921,35923,35925,35927,35929,35931,35933,35935,35937,35940,35942,35944,35946,35948,35950,35952,35954,35956,35958,35960,35962,35964,35966,35968,35970,35972,35974,35976,35978,35980,35982,35984,35986,35988,35990,35992,35994,35996,35998,36000,36002,36004,36006,36008,36010,36012,36014,36016,36018,36020,36022,36024,36026,36028,36030,36032,36035,36037,36039,36041,36043,36045,36047,36049,36051,36053,36055,36057,36059,36061,36063,36065,36067,36069,36071,36073,36075,36077,36079,36081,36083,36085,36087,36090,36092,36094,36096,36098,36100,36102,36104,36106,36108,36110],{"class":1765,"line":1766},[1107,31406,31407],{"class":1783},"[{",[1107,31409,1970],{"class":1783},[1107,31411,21083],{"class":1775},[1107,31413,1970],{"class":1783},[1107,31415,992],{"class":1783},[1107,31417,1970],{"class":1783},[1107,31419,31420],{"class":1966},"9484c25a0120bd48",[1107,31422,1970],{"class":1783},[1107,31424,1955],{"class":1783},[1107,31426,1970],{"class":1783},[1107,31428,31429],{"class":1775},"type",[1107,31431,1970],{"class":1783},[1107,31433,992],{"class":1783},[1107,31435,1970],{"class":1783},[1107,31437,31438],{"class":1966},"group",[1107,31440,1970],{"class":1783},[1107,31442,1955],{"class":1783},[1107,31444,1970],{"class":1783},[1107,31446,31447],{"class":1775},"z",[1107,31449,1970],{"class":1783},[1107,31451,992],{"class":1783},[1107,31453,1970],{"class":1783},[1107,31455,31456],{"class":1966},"dd95c0bca1101c86",[1107,31458,1970],{"class":1783},[1107,31460,1955],{"class":1783},[1107,31462,1970],{"class":1783},[1107,31464,1914],{"class":1775},[1107,31466,1970],{"class":1783},[1107,31468,992],{"class":1783},[1107,31470,1970],{"class":1783},[1107,31472,31473],{"class":1966},"Automatically outputs random value (temperature in Celcius) between 0 & 25 every second",[1107,31475,1970],{"class":1783},[1107,31477,1955],{"class":1783},[1107,31479,1970],{"class":1783},[1107,31481,2582],{"class":1775},[1107,31483,1970],{"class":1783},[1107,31485,31486],{"class":1783},":{",[1107,31488,1970],{"class":1783},[1107,31490,31491],{"class":10543},"label",[1107,31493,1970],{"class":1783},[1107,31495,992],{"class":1783},[1107,31497,31498],{"class":1779},"!",[1107,31500,1855],{"class":1854},[1107,31502,31503],{"class":1783},"},",[1107,31505,1970],{"class":1783},[1107,31507,31508],{"class":1775},"nodes",[1107,31510,1970],{"class":1783},[1107,31512,31513],{"class":1783},":[",[1107,31515,1970],{"class":1783},[1107,31517,31518],{"class":1966},"e4f972f9daad6246",[1107,31520,1970],{"class":1783},[1107,31522,1955],{"class":1783},[1107,31524,1970],{"class":1783},[1107,31526,31527],{"class":1966},"c7fc075a1915e87b",[1107,31529,1970],{"class":1783},[1107,31531,1955],{"class":1783},[1107,31533,1970],{"class":1783},[1107,31535,31536],{"class":1966},"966a772c46dc2888",[1107,31538,1970],{"class":1783},[1107,31540,31541],{"class":1783},"],",[1107,31543,1970],{"class":1783},[1107,31545,31546],{"class":1775},"x",[1107,31548,1970],{"class":1783},[1107,31550,992],{"class":1783},[1107,31552,31553],{"class":1854},"34",[1107,31555,1955],{"class":1783},[1107,31557,1970],{"class":1783},[1107,31559,31560],{"class":1775},"y",[1107,31562,1970],{"class":1783},[1107,31564,992],{"class":1783},[1107,31566,31567],{"class":1854},"59",[1107,31569,1955],{"class":1783},[1107,31571,1970],{"class":1783},[1107,31573,31574],{"class":1775},"w",[1107,31576,1970],{"class":1783},[1107,31578,992],{"class":1783},[1107,31580,31581],{"class":1854},"574",[1107,31583,1955],{"class":1783},[1107,31585,1970],{"class":1783},[1107,31587,31588],{"class":1775},"h",[1107,31590,1970],{"class":1783},[1107,31592,992],{"class":1783},[1107,31594,31595],{"class":1854},"82",[1107,31597,31598],{"class":1783},"},{",[1107,31600,1970],{"class":1783},[1107,31602,21083],{"class":1775},[1107,31604,1970],{"class":1783},[1107,31606,992],{"class":1783},[1107,31608,1970],{"class":1783},[1107,31610,31518],{"class":1966},[1107,31612,1970],{"class":1783},[1107,31614,1955],{"class":1783},[1107,31616,1970],{"class":1783},[1107,31618,31429],{"class":1775},[1107,31620,1970],{"class":1783},[1107,31622,992],{"class":1783},[1107,31624,1970],{"class":1783},[1107,31626,31627],{"class":1966},"link out",[1107,31629,1970],{"class":1783},[1107,31631,1955],{"class":1783},[1107,31633,1970],{"class":1783},[1107,31635,31447],{"class":1775},[1107,31637,1970],{"class":1783},[1107,31639,992],{"class":1783},[1107,31641,1970],{"class":1783},[1107,31643,31456],{"class":1966},[1107,31645,1970],{"class":1783},[1107,31647,1955],{"class":1783},[1107,31649,1970],{"class":1783},[1107,31651,31652],{"class":1775},"g",[1107,31654,1970],{"class":1783},[1107,31656,992],{"class":1783},[1107,31658,1970],{"class":1783},[1107,31660,31420],{"class":1966},[1107,31662,1970],{"class":1783},[1107,31664,1955],{"class":1783},[1107,31666,1970],{"class":1783},[1107,31668,1914],{"class":1775},[1107,31670,1970],{"class":1783},[1107,31672,992],{"class":1783},[1107,31674,1970],{"class":1783},[1107,31676,31677],{"class":1966},"link out 1",[1107,31679,1970],{"class":1783},[1107,31681,1955],{"class":1783},[1107,31683,1970],{"class":1783},[1107,31685,31686],{"class":1775},"mode",[1107,31688,1970],{"class":1783},[1107,31690,992],{"class":1783},[1107,31692,1970],{"class":1783},[1107,31694,31695],{"class":1966},"link",[1107,31697,1970],{"class":1783},[1107,31699,1955],{"class":1783},[1107,31701,1970],{"class":1783},[1107,31703,31704],{"class":1775},"links",[1107,31706,1970],{"class":1783},[1107,31708,31513],{"class":1783},[1107,31710,1970],{"class":1783},[1107,31712,31713],{"class":1966},"33594c64783cdc45",[1107,31715,1970],{"class":1783},[1107,31717,1955],{"class":1783},[1107,31719,1970],{"class":1783},[1107,31721,31722],{"class":1966},"e6bf7b494b48861e",[1107,31724,1970],{"class":1783},[1107,31726,31541],{"class":1783},[1107,31728,1970],{"class":1783},[1107,31730,31546],{"class":1775},[1107,31732,1970],{"class":1783},[1107,31734,992],{"class":1783},[1107,31736,31737],{"class":1854},"355",[1107,31739,1955],{"class":1783},[1107,31741,1970],{"class":1783},[1107,31743,31560],{"class":1775},[1107,31745,1970],{"class":1783},[1107,31747,992],{"class":1783},[1107,31749,31750],{"class":1854},"100",[1107,31752,1955],{"class":1783},[1107,31754,1970],{"class":1783},[1107,31756,31757],{"class":1775},"wires",[1107,31759,1970],{"class":1783},[1107,31761,31762],{"class":1783},":[]},{",[1107,31764,1970],{"class":1783},[1107,31766,21083],{"class":1775},[1107,31768,1970],{"class":1783},[1107,31770,992],{"class":1783},[1107,31772,1970],{"class":1783},[1107,31774,31527],{"class":1966},[1107,31776,1970],{"class":1783},[1107,31778,1955],{"class":1783},[1107,31780,1970],{"class":1783},[1107,31782,31429],{"class":1775},[1107,31784,1970],{"class":1783},[1107,31786,992],{"class":1783},[1107,31788,1970],{"class":1783},[1107,31790,1588],{"class":1966},[1107,31792,1970],{"class":1783},[1107,31794,1955],{"class":1783},[1107,31796,1970],{"class":1783},[1107,31798,31447],{"class":1775},[1107,31800,1970],{"class":1783},[1107,31802,992],{"class":1783},[1107,31804,1970],{"class":1783},[1107,31806,31456],{"class":1966},[1107,31808,1970],{"class":1783},[1107,31810,1955],{"class":1783},[1107,31812,1970],{"class":1783},[1107,31814,31652],{"class":1775},[1107,31816,1970],{"class":1783},[1107,31818,992],{"class":1783},[1107,31820,1970],{"class":1783},[1107,31822,31420],{"class":1966},[1107,31824,1970],{"class":1783},[1107,31826,1955],{"class":1783},[1107,31828,1970],{"class":1783},[1107,31830,1914],{"class":1775},[1107,31832,1970],{"class":1783},[1107,31834,992],{"class":1783},[1107,31836,26816],{"class":1783},[1107,31838,1955],{"class":1783},[1107,31840,1970],{"class":1783},[1107,31842,31843],{"class":1775},"props",[1107,31845,1970],{"class":1783},[1107,31847,31848],{"class":1783},":[],",[1107,31850,1970],{"class":1783},[1107,31852,31853],{"class":1775},"repeat",[1107,31855,1970],{"class":1783},[1107,31857,992],{"class":1783},[1107,31859,1970],{"class":1783},[1107,31861,4944],{"class":1966},[1107,31863,1970],{"class":1783},[1107,31865,1955],{"class":1783},[1107,31867,1970],{"class":1783},[1107,31869,31870],{"class":1775},"crontab",[1107,31872,1970],{"class":1783},[1107,31874,992],{"class":1783},[1107,31876,26816],{"class":1783},[1107,31878,1955],{"class":1783},[1107,31880,1970],{"class":1783},[1107,31882,31883],{"class":1775},"once",[1107,31885,1970],{"class":1783},[1107,31887,992],{"class":1783},[1107,31889,31498],{"class":1779},[1107,31891,4944],{"class":1854},[1107,31893,1955],{"class":1783},[1107,31895,1970],{"class":1783},[1107,31897,31898],{"class":1775},"onceDelay",[1107,31900,1970],{"class":1783},[1107,31902,992],{"class":1783},[1107,31904,31905],{"class":1854},"0.1",[1107,31907,1955],{"class":1783},[1107,31909,1970],{"class":1783},[1107,31911,31912],{"class":1775},"topic",[1107,31914,1970],{"class":1783},[1107,31916,992],{"class":1783},[1107,31918,26816],{"class":1783},[1107,31920,1955],{"class":1783},[1107,31922,1970],{"class":1783},[1107,31924,31546],{"class":1775},[1107,31926,1970],{"class":1783},[1107,31928,992],{"class":1783},[1107,31930,31931],{"class":1854},"130",[1107,31933,1955],{"class":1783},[1107,31935,1970],{"class":1783},[1107,31937,31560],{"class":1775},[1107,31939,1970],{"class":1783},[1107,31941,992],{"class":1783},[1107,31943,31750],{"class":1854},[1107,31945,1955],{"class":1783},[1107,31947,1970],{"class":1783},[1107,31949,31757],{"class":1775},[1107,31951,1970],{"class":1783},[1107,31953,31954],{"class":1783},":[[",[1107,31956,1970],{"class":1783},[1107,31958,31536],{"class":1966},[1107,31960,1970],{"class":1783},[1107,31962,31963],{"class":1783},"]]},{",[1107,31965,1970],{"class":1783},[1107,31967,21083],{"class":1775},[1107,31969,1970],{"class":1783},[1107,31971,992],{"class":1783},[1107,31973,1970],{"class":1783},[1107,31975,31536],{"class":1966},[1107,31977,1970],{"class":1783},[1107,31979,1955],{"class":1783},[1107,31981,1970],{"class":1783},[1107,31983,31429],{"class":1775},[1107,31985,1970],{"class":1783},[1107,31987,992],{"class":1783},[1107,31989,1970],{"class":1783},[1107,31991,27110],{"class":1966},[1107,31993,1970],{"class":1783},[1107,31995,1955],{"class":1783},[1107,31997,1970],{"class":1783},[1107,31999,31447],{"class":1775},[1107,32001,1970],{"class":1783},[1107,32003,992],{"class":1783},[1107,32005,1970],{"class":1783},[1107,32007,31456],{"class":1966},[1107,32009,1970],{"class":1783},[1107,32011,1955],{"class":1783},[1107,32013,1970],{"class":1783},[1107,32015,31652],{"class":1775},[1107,32017,1970],{"class":1783},[1107,32019,992],{"class":1783},[1107,32021,1970],{"class":1783},[1107,32023,31420],{"class":1966},[1107,32025,1970],{"class":1783},[1107,32027,1955],{"class":1783},[1107,32029,1970],{"class":1783},[1107,32031,1914],{"class":1775},[1107,32033,1970],{"class":1783},[1107,32035,992],{"class":1783},[1107,32037,26816],{"class":1783},[1107,32039,1955],{"class":1783},[1107,32041,1970],{"class":1783},[1107,32043,32044],{"class":1775},"low",[1107,32046,1970],{"class":1783},[1107,32048,992],{"class":1783},[1107,32050,1970],{"class":1783},[1107,32052,1855],{"class":1966},[1107,32054,1970],{"class":1783},[1107,32056,1955],{"class":1783},[1107,32058,1970],{"class":1783},[1107,32060,32061],{"class":1775},"high",[1107,32063,1970],{"class":1783},[1107,32065,992],{"class":1783},[1107,32067,1970],{"class":1783},[1107,32069,32070],{"class":1966},"25",[1107,32072,1970],{"class":1783},[1107,32074,1955],{"class":1783},[1107,32076,1970],{"class":1783},[1107,32078,32079],{"class":1775},"inte",[1107,32081,1970],{"class":1783},[1107,32083,992],{"class":1783},[1107,32085,1970],{"class":1783},[1107,32087,1278],{"class":1966},[1107,32089,1970],{"class":1783},[1107,32091,1955],{"class":1783},[1107,32093,1970],{"class":1783},[1107,32095,32096],{"class":1775},"property",[1107,32098,1970],{"class":1783},[1107,32100,992],{"class":1783},[1107,32102,1970],{"class":1783},[1107,32104,1792],{"class":1966},[1107,32106,1970],{"class":1783},[1107,32108,1955],{"class":1783},[1107,32110,1970],{"class":1783},[1107,32112,31546],{"class":1775},[1107,32114,1970],{"class":1783},[1107,32116,992],{"class":1783},[1107,32118,32119],{"class":1854},"260",[1107,32121,1955],{"class":1783},[1107,32123,1970],{"class":1783},[1107,32125,31560],{"class":1775},[1107,32127,1970],{"class":1783},[1107,32129,992],{"class":1783},[1107,32131,31750],{"class":1854},[1107,32133,1955],{"class":1783},[1107,32135,1970],{"class":1783},[1107,32137,31757],{"class":1775},[1107,32139,1970],{"class":1783},[1107,32141,31954],{"class":1783},[1107,32143,1970],{"class":1783},[1107,32145,31518],{"class":1966},[1107,32147,1970],{"class":1783},[1107,32149,31963],{"class":1783},[1107,32151,1970],{"class":1783},[1107,32153,21083],{"class":1775},[1107,32155,1970],{"class":1783},[1107,32157,992],{"class":1783},[1107,32159,1970],{"class":1783},[1107,32161,32162],{"class":1966},"37380f26e8bfc98a",[1107,32164,1970],{"class":1783},[1107,32166,1955],{"class":1783},[1107,32168,1970],{"class":1783},[1107,32170,31429],{"class":1775},[1107,32172,1970],{"class":1783},[1107,32174,992],{"class":1783},[1107,32176,1970],{"class":1783},[1107,32178,31438],{"class":1966},[1107,32180,1970],{"class":1783},[1107,32182,1955],{"class":1783},[1107,32184,1970],{"class":1783},[1107,32186,31447],{"class":1775},[1107,32188,1970],{"class":1783},[1107,32190,992],{"class":1783},[1107,32192,1970],{"class":1783},[1107,32194,31456],{"class":1966},[1107,32196,1970],{"class":1783},[1107,32198,1955],{"class":1783},[1107,32200,1970],{"class":1783},[1107,32202,1914],{"class":1775},[1107,32204,1970],{"class":1783},[1107,32206,992],{"class":1783},[1107,32208,1970],{"class":1783},[1107,32210,32211],{"class":1966},"Calculate average, high and low, save to flow",[1107,32213,1970],{"class":1783},[1107,32215,1955],{"class":1783},[1107,32217,1970],{"class":1783},[1107,32219,2582],{"class":1775},[1107,32221,1970],{"class":1783},[1107,32223,31486],{"class":1783},[1107,32225,1970],{"class":1783},[1107,32227,31491],{"class":10543},[1107,32229,1970],{"class":1783},[1107,32231,992],{"class":1783},[1107,32233,31498],{"class":1779},[1107,32235,1855],{"class":1854},[1107,32237,31503],{"class":1783},[1107,32239,1970],{"class":1783},[1107,32241,31508],{"class":1775},[1107,32243,1970],{"class":1783},[1107,32245,31513],{"class":1783},[1107,32247,1970],{"class":1783},[1107,32249,32250],{"class":1966},"cc3978c7c4ea56ed",[1107,32252,1970],{"class":1783},[1107,32254,1955],{"class":1783},[1107,32256,1970],{"class":1783},[1107,32258,32259],{"class":1966},"e1819526a5f365c8",[1107,32261,1970],{"class":1783},[1107,32263,1955],{"class":1783},[1107,32265,1970],{"class":1783},[1107,32267,31713],{"class":1966},[1107,32269,1970],{"class":1783},[1107,32271,1955],{"class":1783},[1107,32273,1970],{"class":1783},[1107,32275,32276],{"class":1966},"fea261a15b3b7683",[1107,32278,1970],{"class":1783},[1107,32280,1955],{"class":1783},[1107,32282,1970],{"class":1783},[1107,32284,32285],{"class":1966},"e28a69232f1cac53",[1107,32287,1970],{"class":1783},[1107,32289,1955],{"class":1783},[1107,32291,1970],{"class":1783},[1107,32293,32294],{"class":1966},"aecb1727be523240",[1107,32296,1970],{"class":1783},[1107,32298,1955],{"class":1783},[1107,32300,1970],{"class":1783},[1107,32302,32303],{"class":1966},"e30039ee13e480a8",[1107,32305,1970],{"class":1783},[1107,32307,31541],{"class":1783},[1107,32309,1970],{"class":1783},[1107,32311,31546],{"class":1775},[1107,32313,1970],{"class":1783},[1107,32315,992],{"class":1783},[1107,32317,31553],{"class":1854},[1107,32319,1955],{"class":1783},[1107,32321,1970],{"class":1783},[1107,32323,31560],{"class":1775},[1107,32325,1970],{"class":1783},[1107,32327,992],{"class":1783},[1107,32329,32330],{"class":1854},"259",[1107,32332,1955],{"class":1783},[1107,32334,1970],{"class":1783},[1107,32336,31574],{"class":1775},[1107,32338,1970],{"class":1783},[1107,32340,992],{"class":1783},[1107,32342,32343],{"class":1854},"552",[1107,32345,1955],{"class":1783},[1107,32347,1970],{"class":1783},[1107,32349,31588],{"class":1775},[1107,32351,1970],{"class":1783},[1107,32353,992],{"class":1783},[1107,32355,32356],{"class":1854},"142",[1107,32358,31598],{"class":1783},[1107,32360,1970],{"class":1783},[1107,32362,21083],{"class":1775},[1107,32364,1970],{"class":1783},[1107,32366,992],{"class":1783},[1107,32368,1970],{"class":1783},[1107,32370,32250],{"class":1966},[1107,32372,1970],{"class":1783},[1107,32374,1955],{"class":1783},[1107,32376,1970],{"class":1783},[1107,32378,31429],{"class":1775},[1107,32380,1970],{"class":1783},[1107,32382,992],{"class":1783},[1107,32384,1970],{"class":1783},[1107,32386,9518],{"class":1966},[1107,32388,1970],{"class":1783},[1107,32390,1955],{"class":1783},[1107,32392,1970],{"class":1783},[1107,32394,31447],{"class":1775},[1107,32396,1970],{"class":1783},[1107,32398,992],{"class":1783},[1107,32400,1970],{"class":1783},[1107,32402,31456],{"class":1966},[1107,32404,1970],{"class":1783},[1107,32406,1955],{"class":1783},[1107,32408,1970],{"class":1783},[1107,32410,31652],{"class":1775},[1107,32412,1970],{"class":1783},[1107,32414,992],{"class":1783},[1107,32416,1970],{"class":1783},[1107,32418,32162],{"class":1966},[1107,32420,1970],{"class":1783},[1107,32422,1955],{"class":1783},[1107,32424,1970],{"class":1783},[1107,32426,1914],{"class":1775},[1107,32428,1970],{"class":1783},[1107,32430,992],{"class":1783},[1107,32432,26816],{"class":1783},[1107,32434,1955],{"class":1783},[1107,32436,1970],{"class":1783},[1107,32438,32439],{"class":1775},"rules",[1107,32441,1970],{"class":1783},[1107,32443,32444],{"class":1783},":[{",[1107,32446,1970],{"class":1783},[1107,32448,32449],{"class":10543},"t",[1107,32451,1970],{"class":1783},[1107,32453,992],{"class":1783},[1107,32455,1970],{"class":1783},[1107,32457,15357],{"class":1966},[1107,32459,1970],{"class":1783},[1107,32461,1955],{"class":1783},[1107,32463,1970],{"class":1783},[1107,32465,15],{"class":10543},[1107,32467,1970],{"class":1783},[1107,32469,992],{"class":1783},[1107,32471,1970],{"class":1783},[1107,32473,32061],{"class":1966},[1107,32475,1970],{"class":1783},[1107,32477,1955],{"class":1783},[1107,32479,1970],{"class":1783},[1107,32481,32482],{"class":10543},"pt",[1107,32484,1970],{"class":1783},[1107,32486,992],{"class":1783},[1107,32488,1970],{"class":1783},[1107,32490,15228],{"class":1966},[1107,32492,1970],{"class":1783},[1107,32494,1955],{"class":1783},[1107,32496,1970],{"class":1783},[1107,32498,32499],{"class":10543},"to",[1107,32501,1970],{"class":1783},[1107,32503,992],{"class":1783},[1107,32505,1970],{"class":1783},[1107,32507,1792],{"class":1966},[1107,32509,1970],{"class":1783},[1107,32511,1955],{"class":1783},[1107,32513,1970],{"class":1783},[1107,32515,32516],{"class":10543},"tot",[1107,32518,1970],{"class":1783},[1107,32520,992],{"class":1783},[1107,32522,1970],{"class":1783},[1107,32524,14911],{"class":1966},[1107,32526,1970],{"class":1783},[1107,32528,32529],{"class":1783},"}],",[1107,32531,1970],{"class":1783},[1107,32533,32534],{"class":1775},"action",[1107,32536,1970],{"class":1783},[1107,32538,992],{"class":1783},[1107,32540,26816],{"class":1783},[1107,32542,1955],{"class":1783},[1107,32544,1970],{"class":1783},[1107,32546,32096],{"class":1775},[1107,32548,1970],{"class":1783},[1107,32550,992],{"class":1783},[1107,32552,26816],{"class":1783},[1107,32554,1955],{"class":1783},[1107,32556,1970],{"class":1783},[1107,32558,20214],{"class":1775},[1107,32560,1970],{"class":1783},[1107,32562,992],{"class":1783},[1107,32564,26816],{"class":1783},[1107,32566,1955],{"class":1783},[1107,32568,1970],{"class":1783},[1107,32570,32499],{"class":1775},[1107,32572,1970],{"class":1783},[1107,32574,992],{"class":1783},[1107,32576,26816],{"class":1783},[1107,32578,1955],{"class":1783},[1107,32580,1970],{"class":1783},[1107,32582,32583],{"class":1775},"reg",[1107,32585,1970],{"class":1783},[1107,32587,992],{"class":1783},[1107,32589,31498],{"class":1779},[1107,32591,4944],{"class":1854},[1107,32593,1955],{"class":1783},[1107,32595,1970],{"class":1783},[1107,32597,31546],{"class":1775},[1107,32599,1970],{"class":1783},[1107,32601,992],{"class":1783},[1107,32603,32604],{"class":1854},"310",[1107,32606,1955],{"class":1783},[1107,32608,1970],{"class":1783},[1107,32610,31560],{"class":1775},[1107,32612,1970],{"class":1783},[1107,32614,992],{"class":1783},[1107,32616,32617],{"class":1854},"300",[1107,32619,1955],{"class":1783},[1107,32621,1970],{"class":1783},[1107,32623,31757],{"class":1775},[1107,32625,1970],{"class":1783},[1107,32627,31954],{"class":1783},[1107,32629,1970],{"class":1783},[1107,32631,32294],{"class":1966},[1107,32633,1970],{"class":1783},[1107,32635,31963],{"class":1783},[1107,32637,1970],{"class":1783},[1107,32639,21083],{"class":1775},[1107,32641,1970],{"class":1783},[1107,32643,992],{"class":1783},[1107,32645,1970],{"class":1783},[1107,32647,32259],{"class":1966},[1107,32649,1970],{"class":1783},[1107,32651,1955],{"class":1783},[1107,32653,1970],{"class":1783},[1107,32655,31429],{"class":1775},[1107,32657,1970],{"class":1783},[1107,32659,992],{"class":1783},[1107,32661,1970],{"class":1783},[1107,32663,9518],{"class":1966},[1107,32665,1970],{"class":1783},[1107,32667,1955],{"class":1783},[1107,32669,1970],{"class":1783},[1107,32671,31447],{"class":1775},[1107,32673,1970],{"class":1783},[1107,32675,992],{"class":1783},[1107,32677,1970],{"class":1783},[1107,32679,31456],{"class":1966},[1107,32681,1970],{"class":1783},[1107,32683,1955],{"class":1783},[1107,32685,1970],{"class":1783},[1107,32687,31652],{"class":1775},[1107,32689,1970],{"class":1783},[1107,32691,992],{"class":1783},[1107,32693,1970],{"class":1783},[1107,32695,32162],{"class":1966},[1107,32697,1970],{"class":1783},[1107,32699,1955],{"class":1783},[1107,32701,1970],{"class":1783},[1107,32703,1914],{"class":1775},[1107,32705,1970],{"class":1783},[1107,32707,992],{"class":1783},[1107,32709,26816],{"class":1783},[1107,32711,1955],{"class":1783},[1107,32713,1970],{"class":1783},[1107,32715,32439],{"class":1775},[1107,32717,1970],{"class":1783},[1107,32719,32444],{"class":1783},[1107,32721,1970],{"class":1783},[1107,32723,32449],{"class":10543},[1107,32725,1970],{"class":1783},[1107,32727,992],{"class":1783},[1107,32729,1970],{"class":1783},[1107,32731,15357],{"class":1966},[1107,32733,1970],{"class":1783},[1107,32735,1955],{"class":1783},[1107,32737,1970],{"class":1783},[1107,32739,15],{"class":10543},[1107,32741,1970],{"class":1783},[1107,32743,992],{"class":1783},[1107,32745,1970],{"class":1783},[1107,32747,32044],{"class":1966},[1107,32749,1970],{"class":1783},[1107,32751,1955],{"class":1783},[1107,32753,1970],{"class":1783},[1107,32755,32482],{"class":10543},[1107,32757,1970],{"class":1783},[1107,32759,992],{"class":1783},[1107,32761,1970],{"class":1783},[1107,32763,15228],{"class":1966},[1107,32765,1970],{"class":1783},[1107,32767,1955],{"class":1783},[1107,32769,1970],{"class":1783},[1107,32771,32499],{"class":10543},[1107,32773,1970],{"class":1783},[1107,32775,992],{"class":1783},[1107,32777,1970],{"class":1783},[1107,32779,1792],{"class":1966},[1107,32781,1970],{"class":1783},[1107,32783,1955],{"class":1783},[1107,32785,1970],{"class":1783},[1107,32787,32516],{"class":10543},[1107,32789,1970],{"class":1783},[1107,32791,992],{"class":1783},[1107,32793,1970],{"class":1783},[1107,32795,14911],{"class":1966},[1107,32797,1970],{"class":1783},[1107,32799,32529],{"class":1783},[1107,32801,1970],{"class":1783},[1107,32803,32534],{"class":1775},[1107,32805,1970],{"class":1783},[1107,32807,992],{"class":1783},[1107,32809,26816],{"class":1783},[1107,32811,1955],{"class":1783},[1107,32813,1970],{"class":1783},[1107,32815,32096],{"class":1775},[1107,32817,1970],{"class":1783},[1107,32819,992],{"class":1783},[1107,32821,26816],{"class":1783},[1107,32823,1955],{"class":1783},[1107,32825,1970],{"class":1783},[1107,32827,20214],{"class":1775},[1107,32829,1970],{"class":1783},[1107,32831,992],{"class":1783},[1107,32833,26816],{"class":1783},[1107,32835,1955],{"class":1783},[1107,32837,1970],{"class":1783},[1107,32839,32499],{"class":1775},[1107,32841,1970],{"class":1783},[1107,32843,992],{"class":1783},[1107,32845,26816],{"class":1783},[1107,32847,1955],{"class":1783},[1107,32849,1970],{"class":1783},[1107,32851,32583],{"class":1775},[1107,32853,1970],{"class":1783},[1107,32855,992],{"class":1783},[1107,32857,31498],{"class":1779},[1107,32859,4944],{"class":1854},[1107,32861,1955],{"class":1783},[1107,32863,1970],{"class":1783},[1107,32865,31546],{"class":1775},[1107,32867,1970],{"class":1783},[1107,32869,992],{"class":1783},[1107,32871,32604],{"class":1854},[1107,32873,1955],{"class":1783},[1107,32875,1970],{"class":1783},[1107,32877,31560],{"class":1775},[1107,32879,1970],{"class":1783},[1107,32881,992],{"class":1783},[1107,32883,21515],{"class":1854},[1107,32885,1955],{"class":1783},[1107,32887,1970],{"class":1783},[1107,32889,31757],{"class":1775},[1107,32891,1970],{"class":1783},[1107,32893,31954],{"class":1783},[1107,32895,1970],{"class":1783},[1107,32897,32303],{"class":1966},[1107,32899,1970],{"class":1783},[1107,32901,31963],{"class":1783},[1107,32903,1970],{"class":1783},[1107,32905,21083],{"class":1775},[1107,32907,1970],{"class":1783},[1107,32909,992],{"class":1783},[1107,32911,1970],{"class":1783},[1107,32913,31713],{"class":1966},[1107,32915,1970],{"class":1783},[1107,32917,1955],{"class":1783},[1107,32919,1970],{"class":1783},[1107,32921,31429],{"class":1775},[1107,32923,1970],{"class":1783},[1107,32925,992],{"class":1783},[1107,32927,1970],{"class":1783},[1107,32929,32930],{"class":1966},"link in",[1107,32932,1970],{"class":1783},[1107,32934,1955],{"class":1783},[1107,32936,1970],{"class":1783},[1107,32938,31447],{"class":1775},[1107,32940,1970],{"class":1783},[1107,32942,992],{"class":1783},[1107,32944,1970],{"class":1783},[1107,32946,31456],{"class":1966},[1107,32948,1970],{"class":1783},[1107,32950,1955],{"class":1783},[1107,32952,1970],{"class":1783},[1107,32954,31652],{"class":1775},[1107,32956,1970],{"class":1783},[1107,32958,992],{"class":1783},[1107,32960,1970],{"class":1783},[1107,32962,32162],{"class":1966},[1107,32964,1970],{"class":1783},[1107,32966,1955],{"class":1783},[1107,32968,1970],{"class":1783},[1107,32970,1914],{"class":1775},[1107,32972,1970],{"class":1783},[1107,32974,992],{"class":1783},[1107,32976,1970],{"class":1783},[1107,32978,32979],{"class":1966},"link in 1",[1107,32981,1970],{"class":1783},[1107,32983,1955],{"class":1783},[1107,32985,1970],{"class":1783},[1107,32987,31704],{"class":1775},[1107,32989,1970],{"class":1783},[1107,32991,31513],{"class":1783},[1107,32993,1970],{"class":1783},[1107,32995,32996],{"class":1966},"c07b2e101cecbd3b",[1107,32998,1970],{"class":1783},[1107,33000,1955],{"class":1783},[1107,33002,1970],{"class":1783},[1107,33004,31518],{"class":1966},[1107,33006,1970],{"class":1783},[1107,33008,31541],{"class":1783},[1107,33010,1970],{"class":1783},[1107,33012,31546],{"class":1775},[1107,33014,1970],{"class":1783},[1107,33016,992],{"class":1783},[1107,33018,33019],{"class":1854},"75",[1107,33021,1955],{"class":1783},[1107,33023,1970],{"class":1783},[1107,33025,31560],{"class":1775},[1107,33027,1970],{"class":1783},[1107,33029,992],{"class":1783},[1107,33031,33032],{"class":1854},"320",[1107,33034,1955],{"class":1783},[1107,33036,1970],{"class":1783},[1107,33038,31757],{"class":1775},[1107,33040,1970],{"class":1783},[1107,33042,31954],{"class":1783},[1107,33044,1970],{"class":1783},[1107,33046,32285],{"class":1966},[1107,33048,1970],{"class":1783},[1107,33050,1955],{"class":1783},[1107,33052,1970],{"class":1783},[1107,33054,32276],{"class":1966},[1107,33056,1970],{"class":1783},[1107,33058,31963],{"class":1783},[1107,33060,1970],{"class":1783},[1107,33062,21083],{"class":1775},[1107,33064,1970],{"class":1783},[1107,33066,992],{"class":1783},[1107,33068,1970],{"class":1783},[1107,33070,32276],{"class":1966},[1107,33072,1970],{"class":1783},[1107,33074,1955],{"class":1783},[1107,33076,1970],{"class":1783},[1107,33078,31429],{"class":1775},[1107,33080,1970],{"class":1783},[1107,33082,992],{"class":1783},[1107,33084,1970],{"class":1783},[1107,33086,33087],{"class":1966},"smooth",[1107,33089,1970],{"class":1783},[1107,33091,1955],{"class":1783},[1107,33093,1970],{"class":1783},[1107,33095,31447],{"class":1775},[1107,33097,1970],{"class":1783},[1107,33099,992],{"class":1783},[1107,33101,1970],{"class":1783},[1107,33103,31456],{"class":1966},[1107,33105,1970],{"class":1783},[1107,33107,1955],{"class":1783},[1107,33109,1970],{"class":1783},[1107,33111,31652],{"class":1775},[1107,33113,1970],{"class":1783},[1107,33115,992],{"class":1783},[1107,33117,1970],{"class":1783},[1107,33119,32162],{"class":1966},[1107,33121,1970],{"class":1783},[1107,33123,1955],{"class":1783},[1107,33125,1970],{"class":1783},[1107,33127,1914],{"class":1775},[1107,33129,1970],{"class":1783},[1107,33131,992],{"class":1783},[1107,33133,1970],{"class":1783},[1107,33135,2272],{"class":1966},[1107,33137,1970],{"class":1783},[1107,33139,1955],{"class":1783},[1107,33141,1970],{"class":1783},[1107,33143,32096],{"class":1775},[1107,33145,1970],{"class":1783},[1107,33147,992],{"class":1783},[1107,33149,1970],{"class":1783},[1107,33151,1792],{"class":1966},[1107,33153,1970],{"class":1783},[1107,33155,1955],{"class":1783},[1107,33157,1970],{"class":1783},[1107,33159,32534],{"class":1775},[1107,33161,1970],{"class":1783},[1107,33163,992],{"class":1783},[1107,33165,1970],{"class":1783},[1107,33167,33168],{"class":1966},"min",[1107,33170,1970],{"class":1783},[1107,33172,1955],{"class":1783},[1107,33174,1970],{"class":1783},[1107,33176,33177],{"class":1775},"count",[1107,33179,1970],{"class":1783},[1107,33181,992],{"class":1783},[1107,33183,1970],{"class":1783},[1107,33185,31750],{"class":1966},[1107,33187,1970],{"class":1783},[1107,33189,1955],{"class":1783},[1107,33191,1970],{"class":1783},[1107,33193,17810],{"class":1775},[1107,33195,1970],{"class":1783},[1107,33197,992],{"class":1783},[1107,33199,1970],{"class":1783},[1107,33201,4948],{"class":1966},[1107,33203,1970],{"class":1783},[1107,33205,1955],{"class":1783},[1107,33207,1970],{"class":1783},[1107,33209,33210],{"class":1775},"mult",[1107,33212,1970],{"class":1783},[1107,33214,992],{"class":1783},[1107,33216,1970],{"class":1783},[1107,33218,33219],{"class":1966},"single",[1107,33221,1970],{"class":1783},[1107,33223,1955],{"class":1783},[1107,33225,1970],{"class":1783},[1107,33227,33228],{"class":1775},"reduce",[1107,33230,1970],{"class":1783},[1107,33232,992],{"class":1783},[1107,33234,31498],{"class":1779},[1107,33236,4944],{"class":1854},[1107,33238,1955],{"class":1783},[1107,33240,1970],{"class":1783},[1107,33242,31546],{"class":1775},[1107,33244,1970],{"class":1783},[1107,33246,992],{"class":1783},[1107,33248,33249],{"class":1854},"170",[1107,33251,1955],{"class":1783},[1107,33253,1970],{"class":1783},[1107,33255,31560],{"class":1775},[1107,33257,1970],{"class":1783},[1107,33259,992],{"class":1783},[1107,33261,21515],{"class":1854},[1107,33263,1955],{"class":1783},[1107,33265,1970],{"class":1783},[1107,33267,31757],{"class":1775},[1107,33269,1970],{"class":1783},[1107,33271,31954],{"class":1783},[1107,33273,1970],{"class":1783},[1107,33275,32259],{"class":1966},[1107,33277,1970],{"class":1783},[1107,33279,31963],{"class":1783},[1107,33281,1970],{"class":1783},[1107,33283,21083],{"class":1775},[1107,33285,1970],{"class":1783},[1107,33287,992],{"class":1783},[1107,33289,1970],{"class":1783},[1107,33291,32285],{"class":1966},[1107,33293,1970],{"class":1783},[1107,33295,1955],{"class":1783},[1107,33297,1970],{"class":1783},[1107,33299,31429],{"class":1775},[1107,33301,1970],{"class":1783},[1107,33303,992],{"class":1783},[1107,33305,1970],{"class":1783},[1107,33307,33087],{"class":1966},[1107,33309,1970],{"class":1783},[1107,33311,1955],{"class":1783},[1107,33313,1970],{"class":1783},[1107,33315,31447],{"class":1775},[1107,33317,1970],{"class":1783},[1107,33319,992],{"class":1783},[1107,33321,1970],{"class":1783},[1107,33323,31456],{"class":1966},[1107,33325,1970],{"class":1783},[1107,33327,1955],{"class":1783},[1107,33329,1970],{"class":1783},[1107,33331,31652],{"class":1775},[1107,33333,1970],{"class":1783},[1107,33335,992],{"class":1783},[1107,33337,1970],{"class":1783},[1107,33339,32162],{"class":1966},[1107,33341,1970],{"class":1783},[1107,33343,1955],{"class":1783},[1107,33345,1970],{"class":1783},[1107,33347,1914],{"class":1775},[1107,33349,1970],{"class":1783},[1107,33351,992],{"class":1783},[1107,33353,1970],{"class":1783},[1107,33355,2276],{"class":1966},[1107,33357,1970],{"class":1783},[1107,33359,1955],{"class":1783},[1107,33361,1970],{"class":1783},[1107,33363,32096],{"class":1775},[1107,33365,1970],{"class":1783},[1107,33367,992],{"class":1783},[1107,33369,1970],{"class":1783},[1107,33371,1792],{"class":1966},[1107,33373,1970],{"class":1783},[1107,33375,1955],{"class":1783},[1107,33377,1970],{"class":1783},[1107,33379,32534],{"class":1775},[1107,33381,1970],{"class":1783},[1107,33383,992],{"class":1783},[1107,33385,1970],{"class":1783},[1107,33387,33388],{"class":1966},"max",[1107,33390,1970],{"class":1783},[1107,33392,1955],{"class":1783},[1107,33394,1970],{"class":1783},[1107,33396,33177],{"class":1775},[1107,33398,1970],{"class":1783},[1107,33400,992],{"class":1783},[1107,33402,1970],{"class":1783},[1107,33404,31750],{"class":1966},[1107,33406,1970],{"class":1783},[1107,33408,1955],{"class":1783},[1107,33410,1970],{"class":1783},[1107,33412,17810],{"class":1775},[1107,33414,1970],{"class":1783},[1107,33416,992],{"class":1783},[1107,33418,1970],{"class":1783},[1107,33420,4948],{"class":1966},[1107,33422,1970],{"class":1783},[1107,33424,1955],{"class":1783},[1107,33426,1970],{"class":1783},[1107,33428,33210],{"class":1775},[1107,33430,1970],{"class":1783},[1107,33432,992],{"class":1783},[1107,33434,1970],{"class":1783},[1107,33436,33219],{"class":1966},[1107,33438,1970],{"class":1783},[1107,33440,1955],{"class":1783},[1107,33442,1970],{"class":1783},[1107,33444,33228],{"class":1775},[1107,33446,1970],{"class":1783},[1107,33448,992],{"class":1783},[1107,33450,31498],{"class":1779},[1107,33452,4944],{"class":1854},[1107,33454,1955],{"class":1783},[1107,33456,1970],{"class":1783},[1107,33458,31546],{"class":1775},[1107,33460,1970],{"class":1783},[1107,33462,992],{"class":1783},[1107,33464,33249],{"class":1854},[1107,33466,1955],{"class":1783},[1107,33468,1970],{"class":1783},[1107,33470,31560],{"class":1775},[1107,33472,1970],{"class":1783},[1107,33474,992],{"class":1783},[1107,33476,32617],{"class":1854},[1107,33478,1955],{"class":1783},[1107,33480,1970],{"class":1783},[1107,33482,31757],{"class":1775},[1107,33484,1970],{"class":1783},[1107,33486,31954],{"class":1783},[1107,33488,1970],{"class":1783},[1107,33490,32250],{"class":1966},[1107,33492,1970],{"class":1783},[1107,33494,31963],{"class":1783},[1107,33496,1970],{"class":1783},[1107,33498,21083],{"class":1775},[1107,33500,1970],{"class":1783},[1107,33502,992],{"class":1783},[1107,33504,1970],{"class":1783},[1107,33506,32294],{"class":1966},[1107,33508,1970],{"class":1783},[1107,33510,1955],{"class":1783},[1107,33512,1970],{"class":1783},[1107,33514,31429],{"class":1775},[1107,33516,1970],{"class":1783},[1107,33518,992],{"class":1783},[1107,33520,1970],{"class":1783},[1107,33522,1634],{"class":1966},[1107,33524,1970],{"class":1783},[1107,33526,1955],{"class":1783},[1107,33528,1970],{"class":1783},[1107,33530,31447],{"class":1775},[1107,33532,1970],{"class":1783},[1107,33534,992],{"class":1783},[1107,33536,1970],{"class":1783},[1107,33538,31456],{"class":1966},[1107,33540,1970],{"class":1783},[1107,33542,1955],{"class":1783},[1107,33544,1970],{"class":1783},[1107,33546,31652],{"class":1775},[1107,33548,1970],{"class":1783},[1107,33550,992],{"class":1783},[1107,33552,1970],{"class":1783},[1107,33554,32162],{"class":1966},[1107,33556,1970],{"class":1783},[1107,33558,1955],{"class":1783},[1107,33560,1970],{"class":1783},[1107,33562,1914],{"class":1775},[1107,33564,1970],{"class":1783},[1107,33566,992],{"class":1783},[1107,33568,1970],{"class":1783},[1107,33570,33571],{"class":1966},"debug 23",[1107,33573,1970],{"class":1783},[1107,33575,1955],{"class":1783},[1107,33577,1970],{"class":1783},[1107,33579,15406],{"class":1775},[1107,33581,1970],{"class":1783},[1107,33583,992],{"class":1783},[1107,33585,31498],{"class":1779},[1107,33587,1855],{"class":1854},[1107,33589,1955],{"class":1783},[1107,33591,1970],{"class":1783},[1107,33593,33594],{"class":1775},"tosidebar",[1107,33596,1970],{"class":1783},[1107,33598,992],{"class":1783},[1107,33600,31498],{"class":1779},[1107,33602,4944],{"class":1854},[1107,33604,1955],{"class":1783},[1107,33606,1970],{"class":1783},[1107,33608,33609],{"class":1775},"console",[1107,33611,1970],{"class":1783},[1107,33613,992],{"class":1783},[1107,33615,31498],{"class":1779},[1107,33617,4944],{"class":1854},[1107,33619,1955],{"class":1783},[1107,33621,1970],{"class":1783},[1107,33623,33624],{"class":1775},"tostatus",[1107,33626,1970],{"class":1783},[1107,33628,992],{"class":1783},[1107,33630,31498],{"class":1779},[1107,33632,1855],{"class":1854},[1107,33634,1955],{"class":1783},[1107,33636,1970],{"class":1783},[1107,33638,33639],{"class":1775},"complete",[1107,33641,1970],{"class":1783},[1107,33643,992],{"class":1783},[1107,33645,1970],{"class":1783},[1107,33647,1792],{"class":1966},[1107,33649,1970],{"class":1783},[1107,33651,1955],{"class":1783},[1107,33653,1970],{"class":1783},[1107,33655,33656],{"class":1775},"targetType",[1107,33658,1970],{"class":1783},[1107,33660,992],{"class":1783},[1107,33662,1970],{"class":1783},[1107,33664,14911],{"class":1966},[1107,33666,1970],{"class":1783},[1107,33668,1955],{"class":1783},[1107,33670,1970],{"class":1783},[1107,33672,33673],{"class":1775},"statusVal",[1107,33675,1970],{"class":1783},[1107,33677,992],{"class":1783},[1107,33679,1970],{"class":1783},[1107,33681,1792],{"class":1966},[1107,33683,1970],{"class":1783},[1107,33685,1955],{"class":1783},[1107,33687,1970],{"class":1783},[1107,33689,33690],{"class":1775},"statusType",[1107,33692,1970],{"class":1783},[1107,33694,992],{"class":1783},[1107,33696,1970],{"class":1783},[1107,33698,33699],{"class":1966},"auto",[1107,33701,1970],{"class":1783},[1107,33703,1955],{"class":1783},[1107,33705,1970],{"class":1783},[1107,33707,31546],{"class":1775},[1107,33709,1970],{"class":1783},[1107,33711,992],{"class":1783},[1107,33713,33714],{"class":1854},"480",[1107,33716,1955],{"class":1783},[1107,33718,1970],{"class":1783},[1107,33720,31560],{"class":1775},[1107,33722,1970],{"class":1783},[1107,33724,992],{"class":1783},[1107,33726,32617],{"class":1854},[1107,33728,1955],{"class":1783},[1107,33730,1970],{"class":1783},[1107,33732,31757],{"class":1775},[1107,33734,1970],{"class":1783},[1107,33736,31762],{"class":1783},[1107,33738,1970],{"class":1783},[1107,33740,21083],{"class":1775},[1107,33742,1970],{"class":1783},[1107,33744,992],{"class":1783},[1107,33746,1970],{"class":1783},[1107,33748,32303],{"class":1966},[1107,33750,1970],{"class":1783},[1107,33752,1955],{"class":1783},[1107,33754,1970],{"class":1783},[1107,33756,31429],{"class":1775},[1107,33758,1970],{"class":1783},[1107,33760,992],{"class":1783},[1107,33762,1970],{"class":1783},[1107,33764,1634],{"class":1966},[1107,33766,1970],{"class":1783},[1107,33768,1955],{"class":1783},[1107,33770,1970],{"class":1783},[1107,33772,31447],{"class":1775},[1107,33774,1970],{"class":1783},[1107,33776,992],{"class":1783},[1107,33778,1970],{"class":1783},[1107,33780,31456],{"class":1966},[1107,33782,1970],{"class":1783},[1107,33784,1955],{"class":1783},[1107,33786,1970],{"class":1783},[1107,33788,31652],{"class":1775},[1107,33790,1970],{"class":1783},[1107,33792,992],{"class":1783},[1107,33794,1970],{"class":1783},[1107,33796,32162],{"class":1966},[1107,33798,1970],{"class":1783},[1107,33800,1955],{"class":1783},[1107,33802,1970],{"class":1783},[1107,33804,1914],{"class":1775},[1107,33806,1970],{"class":1783},[1107,33808,992],{"class":1783},[1107,33810,1970],{"class":1783},[1107,33812,33813],{"class":1966},"debug 25",[1107,33815,1970],{"class":1783},[1107,33817,1955],{"class":1783},[1107,33819,1970],{"class":1783},[1107,33821,15406],{"class":1775},[1107,33823,1970],{"class":1783},[1107,33825,992],{"class":1783},[1107,33827,31498],{"class":1779},[1107,33829,1855],{"class":1854},[1107,33831,1955],{"class":1783},[1107,33833,1970],{"class":1783},[1107,33835,33594],{"class":1775},[1107,33837,1970],{"class":1783},[1107,33839,992],{"class":1783},[1107,33841,31498],{"class":1779},[1107,33843,4944],{"class":1854},[1107,33845,1955],{"class":1783},[1107,33847,1970],{"class":1783},[1107,33849,33609],{"class":1775},[1107,33851,1970],{"class":1783},[1107,33853,992],{"class":1783},[1107,33855,31498],{"class":1779},[1107,33857,4944],{"class":1854},[1107,33859,1955],{"class":1783},[1107,33861,1970],{"class":1783},[1107,33863,33624],{"class":1775},[1107,33865,1970],{"class":1783},[1107,33867,992],{"class":1783},[1107,33869,31498],{"class":1779},[1107,33871,1855],{"class":1854},[1107,33873,1955],{"class":1783},[1107,33875,1970],{"class":1783},[1107,33877,33639],{"class":1775},[1107,33879,1970],{"class":1783},[1107,33881,992],{"class":1783},[1107,33883,1970],{"class":1783},[1107,33885,1792],{"class":1966},[1107,33887,1970],{"class":1783},[1107,33889,1955],{"class":1783},[1107,33891,1970],{"class":1783},[1107,33893,33656],{"class":1775},[1107,33895,1970],{"class":1783},[1107,33897,992],{"class":1783},[1107,33899,1970],{"class":1783},[1107,33901,14911],{"class":1966},[1107,33903,1970],{"class":1783},[1107,33905,1955],{"class":1783},[1107,33907,1970],{"class":1783},[1107,33909,33673],{"class":1775},[1107,33911,1970],{"class":1783},[1107,33913,992],{"class":1783},[1107,33915,1970],{"class":1783},[1107,33917,1792],{"class":1966},[1107,33919,1970],{"class":1783},[1107,33921,1955],{"class":1783},[1107,33923,1970],{"class":1783},[1107,33925,33690],{"class":1775},[1107,33927,1970],{"class":1783},[1107,33929,992],{"class":1783},[1107,33931,1970],{"class":1783},[1107,33933,33699],{"class":1966},[1107,33935,1970],{"class":1783},[1107,33937,1955],{"class":1783},[1107,33939,1970],{"class":1783},[1107,33941,31546],{"class":1775},[1107,33943,1970],{"class":1783},[1107,33945,992],{"class":1783},[1107,33947,33714],{"class":1854},[1107,33949,1955],{"class":1783},[1107,33951,1970],{"class":1783},[1107,33953,31560],{"class":1775},[1107,33955,1970],{"class":1783},[1107,33957,992],{"class":1783},[1107,33959,21515],{"class":1854},[1107,33961,1955],{"class":1783},[1107,33963,1970],{"class":1783},[1107,33965,31757],{"class":1775},[1107,33967,1970],{"class":1783},[1107,33969,31762],{"class":1783},[1107,33971,1970],{"class":1783},[1107,33973,21083],{"class":1775},[1107,33975,1970],{"class":1783},[1107,33977,992],{"class":1783},[1107,33979,1970],{"class":1783},[1107,33981,33982],{"class":1966},"ec11a9ee9148b0b5",[1107,33984,1970],{"class":1783},[1107,33986,1955],{"class":1783},[1107,33988,1970],{"class":1783},[1107,33990,31429],{"class":1775},[1107,33992,1970],{"class":1783},[1107,33994,992],{"class":1783},[1107,33996,1970],{"class":1783},[1107,33998,31438],{"class":1966},[1107,34000,1970],{"class":1783},[1107,34002,1955],{"class":1783},[1107,34004,1970],{"class":1783},[1107,34006,31447],{"class":1775},[1107,34008,1970],{"class":1783},[1107,34010,992],{"class":1783},[1107,34012,1970],{"class":1783},[1107,34014,31456],{"class":1966},[1107,34016,1970],{"class":1783},[1107,34018,1955],{"class":1783},[1107,34020,1970],{"class":1783},[1107,34022,1914],{"class":1775},[1107,34024,1970],{"class":1783},[1107,34026,992],{"class":1783},[1107,34028,1970],{"class":1783},[1107,34030,34031],{"class":1966},"Evaluate if an incoming value is between flow.high and flow.low, if it is not, send the message down a different wire and show an alert in debug",[1107,34033,1970],{"class":1783},[1107,34035,1955],{"class":1783},[1107,34037,1970],{"class":1783},[1107,34039,2582],{"class":1775},[1107,34041,1970],{"class":1783},[1107,34043,31486],{"class":1783},[1107,34045,1970],{"class":1783},[1107,34047,31491],{"class":10543},[1107,34049,1970],{"class":1783},[1107,34051,992],{"class":1783},[1107,34053,31498],{"class":1779},[1107,34055,1855],{"class":1854},[1107,34057,31503],{"class":1783},[1107,34059,1970],{"class":1783},[1107,34061,31508],{"class":1775},[1107,34063,1970],{"class":1783},[1107,34065,31513],{"class":1783},[1107,34067,1970],{"class":1783},[1107,34069,34070],{"class":1966},"9393c22b2e3c1ec8",[1107,34072,1970],{"class":1783},[1107,34074,1955],{"class":1783},[1107,34076,1970],{"class":1783},[1107,34078,34079],{"class":1966},"1cd18307cb919159",[1107,34081,1970],{"class":1783},[1107,34083,1955],{"class":1783},[1107,34085,1970],{"class":1783},[1107,34087,34088],{"class":1966},"7c1f474e646765a7",[1107,34090,1970],{"class":1783},[1107,34092,1955],{"class":1783},[1107,34094,1970],{"class":1783},[1107,34096,34097],{"class":1966},"a209cd10f33ec401",[1107,34099,1970],{"class":1783},[1107,34101,1955],{"class":1783},[1107,34103,1970],{"class":1783},[1107,34105,34106],{"class":1966},"f4b15283e55babf4",[1107,34108,1970],{"class":1783},[1107,34110,1955],{"class":1783},[1107,34112,1970],{"class":1783},[1107,34114,31722],{"class":1966},[1107,34116,1970],{"class":1783},[1107,34118,31541],{"class":1783},[1107,34120,1970],{"class":1783},[1107,34122,31546],{"class":1775},[1107,34124,1970],{"class":1783},[1107,34126,992],{"class":1783},[1107,34128,31553],{"class":1854},[1107,34130,1955],{"class":1783},[1107,34132,1970],{"class":1783},[1107,34134,31560],{"class":1775},[1107,34136,1970],{"class":1783},[1107,34138,992],{"class":1783},[1107,34140,34141],{"class":1854},"419",[1107,34143,1955],{"class":1783},[1107,34145,1970],{"class":1783},[1107,34147,31574],{"class":1775},[1107,34149,1970],{"class":1783},[1107,34151,992],{"class":1783},[1107,34153,34154],{"class":1854},"1032",[1107,34156,1955],{"class":1783},[1107,34158,1970],{"class":1783},[1107,34160,31588],{"class":1775},[1107,34162,1970],{"class":1783},[1107,34164,992],{"class":1783},[1107,34166,34167],{"class":1854},"162",[1107,34169,31598],{"class":1783},[1107,34171,1970],{"class":1783},[1107,34173,21083],{"class":1775},[1107,34175,1970],{"class":1783},[1107,34177,992],{"class":1783},[1107,34179,1970],{"class":1783},[1107,34181,34070],{"class":1966},[1107,34183,1970],{"class":1783},[1107,34185,1955],{"class":1783},[1107,34187,1970],{"class":1783},[1107,34189,31429],{"class":1775},[1107,34191,1970],{"class":1783},[1107,34193,992],{"class":1783},[1107,34195,1970],{"class":1783},[1107,34197,9482],{"class":1966},[1107,34199,1970],{"class":1783},[1107,34201,1955],{"class":1783},[1107,34203,1970],{"class":1783},[1107,34205,31447],{"class":1775},[1107,34207,1970],{"class":1783},[1107,34209,992],{"class":1783},[1107,34211,1970],{"class":1783},[1107,34213,31456],{"class":1966},[1107,34215,1970],{"class":1783},[1107,34217,1955],{"class":1783},[1107,34219,1970],{"class":1783},[1107,34221,31652],{"class":1775},[1107,34223,1970],{"class":1783},[1107,34225,992],{"class":1783},[1107,34227,1970],{"class":1783},[1107,34229,33982],{"class":1966},[1107,34231,1970],{"class":1783},[1107,34233,1955],{"class":1783},[1107,34235,1970],{"class":1783},[1107,34237,1914],{"class":1775},[1107,34239,1970],{"class":1783},[1107,34241,992],{"class":1783},[1107,34243,1970],{"class":1783},[1107,34245,34246],{"class":1966},"Was the value between flow.high and flow.low?",[1107,34248,1970],{"class":1783},[1107,34250,1955],{"class":1783},[1107,34252,1970],{"class":1783},[1107,34254,32096],{"class":1775},[1107,34256,1970],{"class":1783},[1107,34258,992],{"class":1783},[1107,34260,1970],{"class":1783},[1107,34262,1792],{"class":1966},[1107,34264,1970],{"class":1783},[1107,34266,1955],{"class":1783},[1107,34268,1970],{"class":1783},[1107,34270,34271],{"class":1775},"propertyType",[1107,34273,1970],{"class":1783},[1107,34275,992],{"class":1783},[1107,34277,1970],{"class":1783},[1107,34279,14911],{"class":1966},[1107,34281,1970],{"class":1783},[1107,34283,1955],{"class":1783},[1107,34285,1970],{"class":1783},[1107,34287,32439],{"class":1775},[1107,34289,1970],{"class":1783},[1107,34291,32444],{"class":1783},[1107,34293,1970],{"class":1783},[1107,34295,32449],{"class":10543},[1107,34297,1970],{"class":1783},[1107,34299,992],{"class":1783},[1107,34301,1970],{"class":1783},[1107,34303,34304],{"class":1966},"btwn",[1107,34306,1970],{"class":1783},[1107,34308,1955],{"class":1783},[1107,34310,1970],{"class":1783},[1107,34312,34313],{"class":10543},"v",[1107,34315,1970],{"class":1783},[1107,34317,992],{"class":1783},[1107,34319,1970],{"class":1783},[1107,34321,32061],{"class":1966},[1107,34323,1970],{"class":1783},[1107,34325,1955],{"class":1783},[1107,34327,1970],{"class":1783},[1107,34329,34330],{"class":10543},"vt",[1107,34332,1970],{"class":1783},[1107,34334,992],{"class":1783},[1107,34336,1970],{"class":1783},[1107,34338,15228],{"class":1966},[1107,34340,1970],{"class":1783},[1107,34342,1955],{"class":1783},[1107,34344,1970],{"class":1783},[1107,34346,34347],{"class":10543},"v2",[1107,34349,1970],{"class":1783},[1107,34351,992],{"class":1783},[1107,34353,1970],{"class":1783},[1107,34355,32044],{"class":1966},[1107,34357,1970],{"class":1783},[1107,34359,1955],{"class":1783},[1107,34361,1970],{"class":1783},[1107,34363,34364],{"class":10543},"v2t",[1107,34366,1970],{"class":1783},[1107,34368,992],{"class":1783},[1107,34370,1970],{"class":1783},[1107,34372,15228],{"class":1966},[1107,34374,1970],{"class":1783},[1107,34376,31598],{"class":1783},[1107,34378,1970],{"class":1783},[1107,34380,32449],{"class":10543},[1107,34382,1970],{"class":1783},[1107,34384,992],{"class":1783},[1107,34386,1970],{"class":1783},[1107,34388,34389],{"class":1966},"else",[1107,34391,1970],{"class":1783},[1107,34393,32529],{"class":1783},[1107,34395,1970],{"class":1783},[1107,34397,34398],{"class":1775},"checkall",[1107,34400,1970],{"class":1783},[1107,34402,992],{"class":1783},[1107,34404,1970],{"class":1783},[1107,34406,1278],{"class":1966},[1107,34408,1970],{"class":1783},[1107,34410,1955],{"class":1783},[1107,34412,1970],{"class":1783},[1107,34414,34415],{"class":1775},"repair",[1107,34417,1970],{"class":1783},[1107,34419,992],{"class":1783},[1107,34421,31498],{"class":1779},[1107,34423,4944],{"class":1854},[1107,34425,1955],{"class":1783},[1107,34427,1970],{"class":1783},[1107,34429,9537],{"class":1775},[1107,34431,1970],{"class":1783},[1107,34433,992],{"class":1783},[1107,34435,4948],{"class":1854},[1107,34437,1955],{"class":1783},[1107,34439,1970],{"class":1783},[1107,34441,31546],{"class":1775},[1107,34443,1970],{"class":1783},[1107,34445,992],{"class":1783},[1107,34447,32617],{"class":1854},[1107,34449,1955],{"class":1783},[1107,34451,1970],{"class":1783},[1107,34453,31560],{"class":1775},[1107,34455,1970],{"class":1783},[1107,34457,992],{"class":1783},[1107,34459,33714],{"class":1854},[1107,34461,1955],{"class":1783},[1107,34463,1970],{"class":1783},[1107,34465,31757],{"class":1775},[1107,34467,1970],{"class":1783},[1107,34469,31954],{"class":1783},[1107,34471,1970],{"class":1783},[1107,34473,34079],{"class":1966},[1107,34475,1970],{"class":1783},[1107,34477,34478],{"class":1783},"],[",[1107,34480,1970],{"class":1783},[1107,34482,34088],{"class":1966},[1107,34484,1970],{"class":1783},[1107,34486,1955],{"class":1783},[1107,34488,1970],{"class":1783},[1107,34490,34097],{"class":1966},[1107,34492,1970],{"class":1783},[1107,34494,31963],{"class":1783},[1107,34496,1970],{"class":1783},[1107,34498,21083],{"class":1775},[1107,34500,1970],{"class":1783},[1107,34502,992],{"class":1783},[1107,34504,1970],{"class":1783},[1107,34506,34079],{"class":1966},[1107,34508,1970],{"class":1783},[1107,34510,1955],{"class":1783},[1107,34512,1970],{"class":1783},[1107,34514,31429],{"class":1775},[1107,34516,1970],{"class":1783},[1107,34518,992],{"class":1783},[1107,34520,1970],{"class":1783},[1107,34522,1634],{"class":1966},[1107,34524,1970],{"class":1783},[1107,34526,1955],{"class":1783},[1107,34528,1970],{"class":1783},[1107,34530,31447],{"class":1775},[1107,34532,1970],{"class":1783},[1107,34534,992],{"class":1783},[1107,34536,1970],{"class":1783},[1107,34538,31456],{"class":1966},[1107,34540,1970],{"class":1783},[1107,34542,1955],{"class":1783},[1107,34544,1970],{"class":1783},[1107,34546,31652],{"class":1775},[1107,34548,1970],{"class":1783},[1107,34550,992],{"class":1783},[1107,34552,1970],{"class":1783},[1107,34554,33982],{"class":1966},[1107,34556,1970],{"class":1783},[1107,34558,1955],{"class":1783},[1107,34560,1970],{"class":1783},[1107,34562,1914],{"class":1775},[1107,34564,1970],{"class":1783},[1107,34566,992],{"class":1783},[1107,34568,1970],{"class":1783},[1107,34570,34571],{"class":1966},"debug 15",[1107,34573,1970],{"class":1783},[1107,34575,1955],{"class":1783},[1107,34577,1970],{"class":1783},[1107,34579,15406],{"class":1775},[1107,34581,1970],{"class":1783},[1107,34583,992],{"class":1783},[1107,34585,31498],{"class":1779},[1107,34587,1855],{"class":1854},[1107,34589,1955],{"class":1783},[1107,34591,1970],{"class":1783},[1107,34593,33594],{"class":1775},[1107,34595,1970],{"class":1783},[1107,34597,992],{"class":1783},[1107,34599,31498],{"class":1779},[1107,34601,1855],{"class":1854},[1107,34603,1955],{"class":1783},[1107,34605,1970],{"class":1783},[1107,34607,33609],{"class":1775},[1107,34609,1970],{"class":1783},[1107,34611,992],{"class":1783},[1107,34613,31498],{"class":1779},[1107,34615,4944],{"class":1854},[1107,34617,1955],{"class":1783},[1107,34619,1970],{"class":1783},[1107,34621,33624],{"class":1775},[1107,34623,1970],{"class":1783},[1107,34625,992],{"class":1783},[1107,34627,31498],{"class":1779},[1107,34629,4944],{"class":1854},[1107,34631,1955],{"class":1783},[1107,34633,1970],{"class":1783},[1107,34635,33639],{"class":1775},[1107,34637,1970],{"class":1783},[1107,34639,992],{"class":1783},[1107,34641,1970],{"class":1783},[1107,34643,1288],{"class":1966},[1107,34645,1970],{"class":1783},[1107,34647,1955],{"class":1783},[1107,34649,1970],{"class":1783},[1107,34651,33673],{"class":1775},[1107,34653,1970],{"class":1783},[1107,34655,992],{"class":1783},[1107,34657,26816],{"class":1783},[1107,34659,1955],{"class":1783},[1107,34661,1970],{"class":1783},[1107,34663,33690],{"class":1775},[1107,34665,1970],{"class":1783},[1107,34667,992],{"class":1783},[1107,34669,1970],{"class":1783},[1107,34671,33699],{"class":1966},[1107,34673,1970],{"class":1783},[1107,34675,1955],{"class":1783},[1107,34677,1970],{"class":1783},[1107,34679,31546],{"class":1775},[1107,34681,1970],{"class":1783},[1107,34683,992],{"class":1783},[1107,34685,34686],{"class":1854},"560",[1107,34688,1955],{"class":1783},[1107,34690,1970],{"class":1783},[1107,34692,31560],{"class":1775},[1107,34694,1970],{"class":1783},[1107,34696,992],{"class":1783},[1107,34698,34699],{"class":1854},"460",[1107,34701,1955],{"class":1783},[1107,34703,1970],{"class":1783},[1107,34705,31757],{"class":1775},[1107,34707,1970],{"class":1783},[1107,34709,31762],{"class":1783},[1107,34711,1970],{"class":1783},[1107,34713,21083],{"class":1775},[1107,34715,1970],{"class":1783},[1107,34717,992],{"class":1783},[1107,34719,1970],{"class":1783},[1107,34721,34088],{"class":1966},[1107,34723,1970],{"class":1783},[1107,34725,1955],{"class":1783},[1107,34727,1970],{"class":1783},[1107,34729,31429],{"class":1775},[1107,34731,1970],{"class":1783},[1107,34733,992],{"class":1783},[1107,34735,1970],{"class":1783},[1107,34737,1634],{"class":1966},[1107,34739,1970],{"class":1783},[1107,34741,1955],{"class":1783},[1107,34743,1970],{"class":1783},[1107,34745,31447],{"class":1775},[1107,34747,1970],{"class":1783},[1107,34749,992],{"class":1783},[1107,34751,1970],{"class":1783},[1107,34753,31456],{"class":1966},[1107,34755,1970],{"class":1783},[1107,34757,1955],{"class":1783},[1107,34759,1970],{"class":1783},[1107,34761,31652],{"class":1775},[1107,34763,1970],{"class":1783},[1107,34765,992],{"class":1783},[1107,34767,1970],{"class":1783},[1107,34769,33982],{"class":1966},[1107,34771,1970],{"class":1783},[1107,34773,1955],{"class":1783},[1107,34775,1970],{"class":1783},[1107,34777,1914],{"class":1775},[1107,34779,1970],{"class":1783},[1107,34781,992],{"class":1783},[1107,34783,1970],{"class":1783},[1107,34785,34786],{"class":1966},"debug 16",[1107,34788,1970],{"class":1783},[1107,34790,1955],{"class":1783},[1107,34792,1970],{"class":1783},[1107,34794,15406],{"class":1775},[1107,34796,1970],{"class":1783},[1107,34798,992],{"class":1783},[1107,34800,31498],{"class":1779},[1107,34802,4944],{"class":1854},[1107,34804,1955],{"class":1783},[1107,34806,1970],{"class":1783},[1107,34808,33594],{"class":1775},[1107,34810,1970],{"class":1783},[1107,34812,992],{"class":1783},[1107,34814,31498],{"class":1779},[1107,34816,1855],{"class":1854},[1107,34818,1955],{"class":1783},[1107,34820,1970],{"class":1783},[1107,34822,33609],{"class":1775},[1107,34824,1970],{"class":1783},[1107,34826,992],{"class":1783},[1107,34828,31498],{"class":1779},[1107,34830,4944],{"class":1854},[1107,34832,1955],{"class":1783},[1107,34834,1970],{"class":1783},[1107,34836,33624],{"class":1775},[1107,34838,1970],{"class":1783},[1107,34840,992],{"class":1783},[1107,34842,31498],{"class":1779},[1107,34844,4944],{"class":1854},[1107,34846,1955],{"class":1783},[1107,34848,1970],{"class":1783},[1107,34850,33639],{"class":1775},[1107,34852,1970],{"class":1783},[1107,34854,992],{"class":1783},[1107,34856,1970],{"class":1783},[1107,34858,1288],{"class":1966},[1107,34860,1970],{"class":1783},[1107,34862,1955],{"class":1783},[1107,34864,1970],{"class":1783},[1107,34866,33673],{"class":1775},[1107,34868,1970],{"class":1783},[1107,34870,992],{"class":1783},[1107,34872,26816],{"class":1783},[1107,34874,1955],{"class":1783},[1107,34876,1970],{"class":1783},[1107,34878,33690],{"class":1775},[1107,34880,1970],{"class":1783},[1107,34882,992],{"class":1783},[1107,34884,1970],{"class":1783},[1107,34886,33699],{"class":1966},[1107,34888,1970],{"class":1783},[1107,34890,1955],{"class":1783},[1107,34892,1970],{"class":1783},[1107,34894,31546],{"class":1775},[1107,34896,1970],{"class":1783},[1107,34898,992],{"class":1783},[1107,34900,34686],{"class":1854},[1107,34902,1955],{"class":1783},[1107,34904,1970],{"class":1783},[1107,34906,31560],{"class":1775},[1107,34908,1970],{"class":1783},[1107,34910,992],{"class":1783},[1107,34912,34913],{"class":1854},"540",[1107,34915,1955],{"class":1783},[1107,34917,1970],{"class":1783},[1107,34919,31757],{"class":1775},[1107,34921,1970],{"class":1783},[1107,34923,31762],{"class":1783},[1107,34925,1970],{"class":1783},[1107,34927,21083],{"class":1775},[1107,34929,1970],{"class":1783},[1107,34931,992],{"class":1783},[1107,34933,1970],{"class":1783},[1107,34935,34097],{"class":1966},[1107,34937,1970],{"class":1783},[1107,34939,1955],{"class":1783},[1107,34941,1970],{"class":1783},[1107,34943,31429],{"class":1775},[1107,34945,1970],{"class":1783},[1107,34947,992],{"class":1783},[1107,34949,1970],{"class":1783},[1107,34951,9518],{"class":1966},[1107,34953,1970],{"class":1783},[1107,34955,1955],{"class":1783},[1107,34957,1970],{"class":1783},[1107,34959,31447],{"class":1775},[1107,34961,1970],{"class":1783},[1107,34963,992],{"class":1783},[1107,34965,1970],{"class":1783},[1107,34967,31456],{"class":1966},[1107,34969,1970],{"class":1783},[1107,34971,1955],{"class":1783},[1107,34973,1970],{"class":1783},[1107,34975,31652],{"class":1775},[1107,34977,1970],{"class":1783},[1107,34979,992],{"class":1783},[1107,34981,1970],{"class":1783},[1107,34983,33982],{"class":1966},[1107,34985,1970],{"class":1783},[1107,34987,1955],{"class":1783},[1107,34989,1970],{"class":1783},[1107,34991,1914],{"class":1775},[1107,34993,1970],{"class":1783},[1107,34995,992],{"class":1783},[1107,34997,1970],{"class":1783},[1107,34999,35000],{"class":1966},"Alert to debug when value is outside of the range",[1107,35002,1970],{"class":1783},[1107,35004,1955],{"class":1783},[1107,35006,1970],{"class":1783},[1107,35008,32439],{"class":1775},[1107,35010,1970],{"class":1783},[1107,35012,32444],{"class":1783},[1107,35014,1970],{"class":1783},[1107,35016,32449],{"class":10543},[1107,35018,1970],{"class":1783},[1107,35020,992],{"class":1783},[1107,35022,1970],{"class":1783},[1107,35024,15357],{"class":1966},[1107,35026,1970],{"class":1783},[1107,35028,1955],{"class":1783},[1107,35030,1970],{"class":1783},[1107,35032,15],{"class":10543},[1107,35034,1970],{"class":1783},[1107,35036,992],{"class":1783},[1107,35038,1970],{"class":1783},[1107,35040,1792],{"class":1966},[1107,35042,1970],{"class":1783},[1107,35044,1955],{"class":1783},[1107,35046,1970],{"class":1783},[1107,35048,32482],{"class":10543},[1107,35050,1970],{"class":1783},[1107,35052,992],{"class":1783},[1107,35054,1970],{"class":1783},[1107,35056,14911],{"class":1966},[1107,35058,1970],{"class":1783},[1107,35060,1955],{"class":1783},[1107,35062,1970],{"class":1783},[1107,35064,32499],{"class":10543},[1107,35066,1970],{"class":1783},[1107,35068,992],{"class":1783},[1107,35070,1970],{"class":1783},[1107,35072,35073],{"class":1966},"The value was outside of the range",[1107,35075,1970],{"class":1783},[1107,35077,1955],{"class":1783},[1107,35079,1970],{"class":1783},[1107,35081,32516],{"class":10543},[1107,35083,1970],{"class":1783},[1107,35085,992],{"class":1783},[1107,35087,1970],{"class":1783},[1107,35089,35090],{"class":1966},"str",[1107,35092,1970],{"class":1783},[1107,35094,32529],{"class":1783},[1107,35096,1970],{"class":1783},[1107,35098,32534],{"class":1775},[1107,35100,1970],{"class":1783},[1107,35102,992],{"class":1783},[1107,35104,26816],{"class":1783},[1107,35106,1955],{"class":1783},[1107,35108,1970],{"class":1783},[1107,35110,32096],{"class":1775},[1107,35112,1970],{"class":1783},[1107,35114,992],{"class":1783},[1107,35116,26816],{"class":1783},[1107,35118,1955],{"class":1783},[1107,35120,1970],{"class":1783},[1107,35122,20214],{"class":1775},[1107,35124,1970],{"class":1783},[1107,35126,992],{"class":1783},[1107,35128,26816],{"class":1783},[1107,35130,1955],{"class":1783},[1107,35132,1970],{"class":1783},[1107,35134,32499],{"class":1775},[1107,35136,1970],{"class":1783},[1107,35138,992],{"class":1783},[1107,35140,26816],{"class":1783},[1107,35142,1955],{"class":1783},[1107,35144,1970],{"class":1783},[1107,35146,32583],{"class":1775},[1107,35148,1970],{"class":1783},[1107,35150,992],{"class":1783},[1107,35152,31498],{"class":1779},[1107,35154,4944],{"class":1854},[1107,35156,1955],{"class":1783},[1107,35158,1970],{"class":1783},[1107,35160,31546],{"class":1775},[1107,35162,1970],{"class":1783},[1107,35164,992],{"class":1783},[1107,35166,35167],{"class":1854},"690",[1107,35169,1955],{"class":1783},[1107,35171,1970],{"class":1783},[1107,35173,31560],{"class":1775},[1107,35175,1970],{"class":1783},[1107,35177,992],{"class":1783},[1107,35179,35180],{"class":1854},"500",[1107,35182,1955],{"class":1783},[1107,35184,1970],{"class":1783},[1107,35186,31757],{"class":1775},[1107,35188,1970],{"class":1783},[1107,35190,31954],{"class":1783},[1107,35192,1970],{"class":1783},[1107,35194,34106],{"class":1966},[1107,35196,1970],{"class":1783},[1107,35198,31963],{"class":1783},[1107,35200,1970],{"class":1783},[1107,35202,21083],{"class":1775},[1107,35204,1970],{"class":1783},[1107,35206,992],{"class":1783},[1107,35208,1970],{"class":1783},[1107,35210,34106],{"class":1966},[1107,35212,1970],{"class":1783},[1107,35214,1955],{"class":1783},[1107,35216,1970],{"class":1783},[1107,35218,31429],{"class":1775},[1107,35220,1970],{"class":1783},[1107,35222,992],{"class":1783},[1107,35224,1970],{"class":1783},[1107,35226,1634],{"class":1966},[1107,35228,1970],{"class":1783},[1107,35230,1955],{"class":1783},[1107,35232,1970],{"class":1783},[1107,35234,31447],{"class":1775},[1107,35236,1970],{"class":1783},[1107,35238,992],{"class":1783},[1107,35240,1970],{"class":1783},[1107,35242,31456],{"class":1966},[1107,35244,1970],{"class":1783},[1107,35246,1955],{"class":1783},[1107,35248,1970],{"class":1783},[1107,35250,31652],{"class":1775},[1107,35252,1970],{"class":1783},[1107,35254,992],{"class":1783},[1107,35256,1970],{"class":1783},[1107,35258,33982],{"class":1966},[1107,35260,1970],{"class":1783},[1107,35262,1955],{"class":1783},[1107,35264,1970],{"class":1783},[1107,35266,1914],{"class":1775},[1107,35268,1970],{"class":1783},[1107,35270,992],{"class":1783},[1107,35272,1970],{"class":1783},[1107,35274,35275],{"class":1966},"debug 17",[1107,35277,1970],{"class":1783},[1107,35279,1955],{"class":1783},[1107,35281,1970],{"class":1783},[1107,35283,15406],{"class":1775},[1107,35285,1970],{"class":1783},[1107,35287,992],{"class":1783},[1107,35289,31498],{"class":1779},[1107,35291,1855],{"class":1854},[1107,35293,1955],{"class":1783},[1107,35295,1970],{"class":1783},[1107,35297,33594],{"class":1775},[1107,35299,1970],{"class":1783},[1107,35301,992],{"class":1783},[1107,35303,31498],{"class":1779},[1107,35305,1855],{"class":1854},[1107,35307,1955],{"class":1783},[1107,35309,1970],{"class":1783},[1107,35311,33609],{"class":1775},[1107,35313,1970],{"class":1783},[1107,35315,992],{"class":1783},[1107,35317,31498],{"class":1779},[1107,35319,4944],{"class":1854},[1107,35321,1955],{"class":1783},[1107,35323,1970],{"class":1783},[1107,35325,33624],{"class":1775},[1107,35327,1970],{"class":1783},[1107,35329,992],{"class":1783},[1107,35331,31498],{"class":1779},[1107,35333,4944],{"class":1854},[1107,35335,1955],{"class":1783},[1107,35337,1970],{"class":1783},[1107,35339,33639],{"class":1775},[1107,35341,1970],{"class":1783},[1107,35343,992],{"class":1783},[1107,35345,1970],{"class":1783},[1107,35347,1288],{"class":1966},[1107,35349,1970],{"class":1783},[1107,35351,1955],{"class":1783},[1107,35353,1970],{"class":1783},[1107,35355,33673],{"class":1775},[1107,35357,1970],{"class":1783},[1107,35359,992],{"class":1783},[1107,35361,26816],{"class":1783},[1107,35363,1955],{"class":1783},[1107,35365,1970],{"class":1783},[1107,35367,33690],{"class":1775},[1107,35369,1970],{"class":1783},[1107,35371,992],{"class":1783},[1107,35373,1970],{"class":1783},[1107,35375,33699],{"class":1966},[1107,35377,1970],{"class":1783},[1107,35379,1955],{"class":1783},[1107,35381,1970],{"class":1783},[1107,35383,31546],{"class":1775},[1107,35385,1970],{"class":1783},[1107,35387,992],{"class":1783},[1107,35389,35390],{"class":1854},"960",[1107,35392,1955],{"class":1783},[1107,35394,1970],{"class":1783},[1107,35396,31560],{"class":1775},[1107,35398,1970],{"class":1783},[1107,35400,992],{"class":1783},[1107,35402,35180],{"class":1854},[1107,35404,1955],{"class":1783},[1107,35406,1970],{"class":1783},[1107,35408,31757],{"class":1775},[1107,35410,1970],{"class":1783},[1107,35412,31762],{"class":1783},[1107,35414,1970],{"class":1783},[1107,35416,21083],{"class":1775},[1107,35418,1970],{"class":1783},[1107,35420,992],{"class":1783},[1107,35422,1970],{"class":1783},[1107,35424,31722],{"class":1966},[1107,35426,1970],{"class":1783},[1107,35428,1955],{"class":1783},[1107,35430,1970],{"class":1783},[1107,35432,31429],{"class":1775},[1107,35434,1970],{"class":1783},[1107,35436,992],{"class":1783},[1107,35438,1970],{"class":1783},[1107,35440,32930],{"class":1966},[1107,35442,1970],{"class":1783},[1107,35444,1955],{"class":1783},[1107,35446,1970],{"class":1783},[1107,35448,31447],{"class":1775},[1107,35450,1970],{"class":1783},[1107,35452,992],{"class":1783},[1107,35454,1970],{"class":1783},[1107,35456,31456],{"class":1966},[1107,35458,1970],{"class":1783},[1107,35460,1955],{"class":1783},[1107,35462,1970],{"class":1783},[1107,35464,31652],{"class":1775},[1107,35466,1970],{"class":1783},[1107,35468,992],{"class":1783},[1107,35470,1970],{"class":1783},[1107,35472,33982],{"class":1966},[1107,35474,1970],{"class":1783},[1107,35476,1955],{"class":1783},[1107,35478,1970],{"class":1783},[1107,35480,1914],{"class":1775},[1107,35482,1970],{"class":1783},[1107,35484,992],{"class":1783},[1107,35486,1970],{"class":1783},[1107,35488,35489],{"class":1966},"link in 2",[1107,35491,1970],{"class":1783},[1107,35493,1955],{"class":1783},[1107,35495,1970],{"class":1783},[1107,35497,31704],{"class":1775},[1107,35499,1970],{"class":1783},[1107,35501,31513],{"class":1783},[1107,35503,1970],{"class":1783},[1107,35505,31518],{"class":1966},[1107,35507,1970],{"class":1783},[1107,35509,1955],{"class":1783},[1107,35511,1970],{"class":1783},[1107,35513,32996],{"class":1966},[1107,35515,1970],{"class":1783},[1107,35517,31541],{"class":1783},[1107,35519,1970],{"class":1783},[1107,35521,31546],{"class":1775},[1107,35523,1970],{"class":1783},[1107,35525,992],{"class":1783},[1107,35527,33019],{"class":1854},[1107,35529,1955],{"class":1783},[1107,35531,1970],{"class":1783},[1107,35533,31560],{"class":1775},[1107,35535,1970],{"class":1783},[1107,35537,992],{"class":1783},[1107,35539,33714],{"class":1854},[1107,35541,1955],{"class":1783},[1107,35543,1970],{"class":1783},[1107,35545,31757],{"class":1775},[1107,35547,1970],{"class":1783},[1107,35549,31954],{"class":1783},[1107,35551,1970],{"class":1783},[1107,35553,34070],{"class":1966},[1107,35555,1970],{"class":1783},[1107,35557,31963],{"class":1783},[1107,35559,1970],{"class":1783},[1107,35561,21083],{"class":1775},[1107,35563,1970],{"class":1783},[1107,35565,992],{"class":1783},[1107,35567,1970],{"class":1783},[1107,35569,35570],{"class":1966},"caf4214602d5f2c9",[1107,35572,1970],{"class":1783},[1107,35574,1955],{"class":1783},[1107,35576,1970],{"class":1783},[1107,35578,31429],{"class":1775},[1107,35580,1970],{"class":1783},[1107,35582,992],{"class":1783},[1107,35584,1970],{"class":1783},[1107,35586,31438],{"class":1966},[1107,35588,1970],{"class":1783},[1107,35590,1955],{"class":1783},[1107,35592,1970],{"class":1783},[1107,35594,31447],{"class":1775},[1107,35596,1970],{"class":1783},[1107,35598,992],{"class":1783},[1107,35600,1970],{"class":1783},[1107,35602,31456],{"class":1966},[1107,35604,1970],{"class":1783},[1107,35606,1955],{"class":1783},[1107,35608,1970],{"class":1783},[1107,35610,1914],{"class":1775},[1107,35612,1970],{"class":1783},[1107,35614,992],{"class":1783},[1107,35616,1970],{"class":1783},[1107,35618,35619],{"class":1966},"Manually send a spurious value",[1107,35621,1970],{"class":1783},[1107,35623,1955],{"class":1783},[1107,35625,1970],{"class":1783},[1107,35627,2582],{"class":1775},[1107,35629,1970],{"class":1783},[1107,35631,31486],{"class":1783},[1107,35633,1970],{"class":1783},[1107,35635,31491],{"class":10543},[1107,35637,1970],{"class":1783},[1107,35639,992],{"class":1783},[1107,35641,31498],{"class":1779},[1107,35643,1855],{"class":1854},[1107,35645,31503],{"class":1783},[1107,35647,1970],{"class":1783},[1107,35649,31508],{"class":1775},[1107,35651,1970],{"class":1783},[1107,35653,31513],{"class":1783},[1107,35655,1970],{"class":1783},[1107,35657,35658],{"class":1966},"14097fb7ba3a9ecc",[1107,35660,1970],{"class":1783},[1107,35662,1955],{"class":1783},[1107,35664,1970],{"class":1783},[1107,35666,32996],{"class":1966},[1107,35668,1970],{"class":1783},[1107,35670,31541],{"class":1783},[1107,35672,1970],{"class":1783},[1107,35674,31546],{"class":1775},[1107,35676,1970],{"class":1783},[1107,35678,992],{"class":1783},[1107,35680,31553],{"class":1854},[1107,35682,1955],{"class":1783},[1107,35684,1970],{"class":1783},[1107,35686,31560],{"class":1775},[1107,35688,1970],{"class":1783},[1107,35690,992],{"class":1783},[1107,35692,35693],{"class":1854},"159",[1107,35695,1955],{"class":1783},[1107,35697,1970],{"class":1783},[1107,35699,31574],{"class":1775},[1107,35701,1970],{"class":1783},[1107,35703,992],{"class":1783},[1107,35705,35706],{"class":1854},"232",[1107,35708,1955],{"class":1783},[1107,35710,1970],{"class":1783},[1107,35712,31588],{"class":1775},[1107,35714,1970],{"class":1783},[1107,35716,992],{"class":1783},[1107,35718,31595],{"class":1854},[1107,35720,31598],{"class":1783},[1107,35722,1970],{"class":1783},[1107,35724,21083],{"class":1775},[1107,35726,1970],{"class":1783},[1107,35728,992],{"class":1783},[1107,35730,1970],{"class":1783},[1107,35732,35658],{"class":1966},[1107,35734,1970],{"class":1783},[1107,35736,1955],{"class":1783},[1107,35738,1970],{"class":1783},[1107,35740,31429],{"class":1775},[1107,35742,1970],{"class":1783},[1107,35744,992],{"class":1783},[1107,35746,1970],{"class":1783},[1107,35748,1588],{"class":1966},[1107,35750,1970],{"class":1783},[1107,35752,1955],{"class":1783},[1107,35754,1970],{"class":1783},[1107,35756,31447],{"class":1775},[1107,35758,1970],{"class":1783},[1107,35760,992],{"class":1783},[1107,35762,1970],{"class":1783},[1107,35764,31456],{"class":1966},[1107,35766,1970],{"class":1783},[1107,35768,1955],{"class":1783},[1107,35770,1970],{"class":1783},[1107,35772,31652],{"class":1775},[1107,35774,1970],{"class":1783},[1107,35776,992],{"class":1783},[1107,35778,1970],{"class":1783},[1107,35780,35570],{"class":1966},[1107,35782,1970],{"class":1783},[1107,35784,1955],{"class":1783},[1107,35786,1970],{"class":1783},[1107,35788,1914],{"class":1775},[1107,35790,1970],{"class":1783},[1107,35792,992],{"class":1783},[1107,35794,26816],{"class":1783},[1107,35796,1955],{"class":1783},[1107,35798,1970],{"class":1783},[1107,35800,31843],{"class":1775},[1107,35802,1970],{"class":1783},[1107,35804,32444],{"class":1783},[1107,35806,1970],{"class":1783},[1107,35808,15],{"class":10543},[1107,35810,1970],{"class":1783},[1107,35812,992],{"class":1783},[1107,35814,1970],{"class":1783},[1107,35816,1792],{"class":1966},[1107,35818,1970],{"class":1783},[1107,35820,32529],{"class":1783},[1107,35822,1970],{"class":1783},[1107,35824,31853],{"class":1775},[1107,35826,1970],{"class":1783},[1107,35828,992],{"class":1783},[1107,35830,26816],{"class":1783},[1107,35832,1955],{"class":1783},[1107,35834,1970],{"class":1783},[1107,35836,31870],{"class":1775},[1107,35838,1970],{"class":1783},[1107,35840,992],{"class":1783},[1107,35842,26816],{"class":1783},[1107,35844,1955],{"class":1783},[1107,35846,1970],{"class":1783},[1107,35848,31883],{"class":1775},[1107,35850,1970],{"class":1783},[1107,35852,992],{"class":1783},[1107,35854,31498],{"class":1779},[1107,35856,4944],{"class":1854},[1107,35858,1955],{"class":1783},[1107,35860,1970],{"class":1783},[1107,35862,31898],{"class":1775},[1107,35864,1970],{"class":1783},[1107,35866,992],{"class":1783},[1107,35868,31905],{"class":1854},[1107,35870,1955],{"class":1783},[1107,35872,1970],{"class":1783},[1107,35874,31912],{"class":1775},[1107,35876,1970],{"class":1783},[1107,35878,992],{"class":1783},[1107,35880,26816],{"class":1783},[1107,35882,1955],{"class":1783},[1107,35884,1970],{"class":1783},[1107,35886,1792],{"class":1775},[1107,35888,1970],{"class":1783},[1107,35890,992],{"class":1783},[1107,35892,1970],{"class":1783},[1107,35894,33019],{"class":1966},[1107,35896,1970],{"class":1783},[1107,35898,1955],{"class":1783},[1107,35900,1970],{"class":1783},[1107,35902,35903],{"class":1775},"payloadType",[1107,35905,1970],{"class":1783},[1107,35907,992],{"class":1783},[1107,35909,1970],{"class":1783},[1107,35911,35912],{"class":1966},"num",[1107,35914,1970],{"class":1783},[1107,35916,1955],{"class":1783},[1107,35918,1970],{"class":1783},[1107,35920,31546],{"class":1775},[1107,35922,1970],{"class":1783},[1107,35924,992],{"class":1783},[1107,35926,31931],{"class":1854},[1107,35928,1955],{"class":1783},[1107,35930,1970],{"class":1783},[1107,35932,31560],{"class":1775},[1107,35934,1970],{"class":1783},[1107,35936,992],{"class":1783},[1107,35938,35939],{"class":1854},"200",[1107,35941,1955],{"class":1783},[1107,35943,1970],{"class":1783},[1107,35945,31757],{"class":1775},[1107,35947,1970],{"class":1783},[1107,35949,31954],{"class":1783},[1107,35951,1970],{"class":1783},[1107,35953,32996],{"class":1966},[1107,35955,1970],{"class":1783},[1107,35957,31963],{"class":1783},[1107,35959,1970],{"class":1783},[1107,35961,21083],{"class":1775},[1107,35963,1970],{"class":1783},[1107,35965,992],{"class":1783},[1107,35967,1970],{"class":1783},[1107,35969,32996],{"class":1966},[1107,35971,1970],{"class":1783},[1107,35973,1955],{"class":1783},[1107,35975,1970],{"class":1783},[1107,35977,31429],{"class":1775},[1107,35979,1970],{"class":1783},[1107,35981,992],{"class":1783},[1107,35983,1970],{"class":1783},[1107,35985,31627],{"class":1966},[1107,35987,1970],{"class":1783},[1107,35989,1955],{"class":1783},[1107,35991,1970],{"class":1783},[1107,35993,31447],{"class":1775},[1107,35995,1970],{"class":1783},[1107,35997,992],{"class":1783},[1107,35999,1970],{"class":1783},[1107,36001,31456],{"class":1966},[1107,36003,1970],{"class":1783},[1107,36005,1955],{"class":1783},[1107,36007,1970],{"class":1783},[1107,36009,31652],{"class":1775},[1107,36011,1970],{"class":1783},[1107,36013,992],{"class":1783},[1107,36015,1970],{"class":1783},[1107,36017,35570],{"class":1966},[1107,36019,1970],{"class":1783},[1107,36021,1955],{"class":1783},[1107,36023,1970],{"class":1783},[1107,36025,1914],{"class":1775},[1107,36027,1970],{"class":1783},[1107,36029,992],{"class":1783},[1107,36031,1970],{"class":1783},[1107,36033,36034],{"class":1966},"link out 2",[1107,36036,1970],{"class":1783},[1107,36038,1955],{"class":1783},[1107,36040,1970],{"class":1783},[1107,36042,31686],{"class":1775},[1107,36044,1970],{"class":1783},[1107,36046,992],{"class":1783},[1107,36048,1970],{"class":1783},[1107,36050,31695],{"class":1966},[1107,36052,1970],{"class":1783},[1107,36054,1955],{"class":1783},[1107,36056,1970],{"class":1783},[1107,36058,31704],{"class":1775},[1107,36060,1970],{"class":1783},[1107,36062,31513],{"class":1783},[1107,36064,1970],{"class":1783},[1107,36066,31713],{"class":1966},[1107,36068,1970],{"class":1783},[1107,36070,1955],{"class":1783},[1107,36072,1970],{"class":1783},[1107,36074,31722],{"class":1966},[1107,36076,1970],{"class":1783},[1107,36078,31541],{"class":1783},[1107,36080,1970],{"class":1783},[1107,36082,31546],{"class":1775},[1107,36084,1970],{"class":1783},[1107,36086,992],{"class":1783},[1107,36088,36089],{"class":1854},"225",[1107,36091,1955],{"class":1783},[1107,36093,1970],{"class":1783},[1107,36095,31560],{"class":1775},[1107,36097,1970],{"class":1783},[1107,36099,992],{"class":1783},[1107,36101,35939],{"class":1854},[1107,36103,1955],{"class":1783},[1107,36105,1970],{"class":1783},[1107,36107,31757],{"class":1775},[1107,36109,1970],{"class":1783},[1107,36111,36112],{"class":1783},":[]}]\n",[399,36114,36116],{"id":36115},"_2-perform-simple-maths-functions-using-jsonata-in-change-nodes","2. Perform simple maths functions using JSONata in Change nodes",[15,36118,36119,36120,36123],{},"You can perform basic maths functions using the Change node and JSONata. Let's say you wanted to take a payload and multiply it by a value. You could use a custom node such as ",[63,36121,25970],{"href":25968,"rel":36122},[67]," but you can also easily complete the same task within a change node.",[15,36125,36126],{},[165,36127],{"alt":36128,"src":36129,"title":36128},"Using JSONata in a Change node to multiply a payload by 2.5","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fjsonata.png",[15,36131,36132],{},"This will take the input payload, multiply it by 2.5 then output it as the new payload. You can try this out using the code below.",[15,36134,4917,36135,1150],{},[1107,36136,36137,36138,36140,36141,36144,36145,36147,36148,36150,36151,36153],{},"{\"id\":\"6bbe9c1e81c4ee39\",\"type\":\"inject\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"props\":",[1107,36139,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"2\",\"payloadType\":\"num\",\"x\":350,\"y\":400,\"wires\":[[\"07aa636f3db17775\"]]},{\"id\":\"07aa636f3db17775\",\"type\":\"change\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"rules\":",[1107,36142,36143],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"msg.payload * 2.5\",\"tot\":\"jsonata\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":520,\"y\":440,\"wires\":[[\"8bde558e6e2f8551\"]]},{\"id\":\"8bde558e6e2f8551\",\"type\":\"debug\",\"z\":\"cfe9fec308e144db\",\"name\":\"debug 26\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":680,\"y\":440,\"wires\":",[1107,36146],{},"},{\"id\":\"1c04633997beb150\",\"type\":\"inject\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"props\":",[1107,36149,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"3\",\"payloadType\":\"num\",\"x\":350,\"y\":440,\"wires\":[[\"07aa636f3db17775\"]]},{\"id\":\"bc52c3d2f38115b1\",\"type\":\"inject\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"props\":",[1107,36152,5884],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"4\",\"payloadType\":\"num\",\"x\":350,\"y\":480,\"wires\":[[\"07aa636f3db17775\"]]}",[399,36155,36157],{"id":36156},"_3-use-the-catch-node-to-trigger-flows-on-errors","3. Use the Catch node to trigger flows on errors",[15,36159,36160],{},"Sometimes you might be working with nodes which don't output anything when they error or maybe output text directly to debug. This makes it difficult for you to run flows when something fails. For example, when using the Read File node, where the expected file is not found, it would be useful to be able to run a specific flow which sends an alert.",[15,36162,36163],{},"You can do this using the Catch node. Drop the node onto your workspace then select if you want errors from some or all nodes. For this example I am going to select just the Read File node. If I then rerun the flow I get an error message out of the Catch node every time there is an error with reading the file.",[15,36165,36166],{},[165,36167],{"alt":36168,"src":36169,"title":36168},"Catching an error from the Read File node and outputting a message to debug","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fcatch.gif",[15,36171,36172],{},"Note that there are no wires connecting the flow to the error output. This means you can have a single Catch node monitoring a whole project and logging errors as well as sending alerts as needed. You can import the flows from this example using the code below.",[15,36174,4917,36175,1150],{},[1107,36176,36177,36178,36180,36181,36183,36184,36187,36188,36191],{},"{\"id\":\"d6399c6fddb572ef\",\"type\":\"debug\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"debug 28\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1100,\"y\":300,\"wires\":",[1107,36179],{},"},{\"id\":\"de70fda720070c57\",\"type\":\"inject\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"\",\"props\":",[1107,36182],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":790,\"y\":260,\"wires\":[[\"a18f9c8638c78e57\"]]},{\"id\":\"a18f9c8638c78e57\",\"type\":\"file in\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"\",\"filename\":\"example.txt\",\"filenameType\":\"str\",\"format\":\"utf8\",\"chunk\":false,\"sendError\":false,\"encoding\":\"none\",\"allProps\":false,\"x\":930,\"y\":260,\"wires\":[[]]},{\"id\":\"2dbb0cc4bc10d0bc\",\"type\":\"catch\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"\",\"scope\":",[1107,36185,36186],{},"\"a18f9c8638c78e57\"",",\"uncaught\":false,\"x\":790,\"y\":300,\"wires\":[[\"b22988df6357a52a\"]]},{\"id\":\"b22988df6357a52a\",\"type\":\"change\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"Debug message\",\"rules\":",[1107,36189,36190],{},"{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"There was an error reading the file\",\"tot\":\"str\"}",",\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":940,\"y\":300,\"wires\":[[\"d6399c6fddb572ef\"]]}",[15,36193,29135,36194,29],{},[63,36195,12885],{"href":29138},[399,36197,36199],{"id":36198},"simplifying-multi-instance-communication-with-flowfuse-project-nodes","Simplifying Multi-Instance Communication with FlowFuse Project Nodes",[15,36201,36202,36203,36206],{},"Coordinating communication between multiple Node-RED instances can be challenging, but FlowFuse's ",[63,36204,36205],{"href":18479},"Project Nodes"," make it effortless. With these nodes, you can seamlessly send messages between instances without dealing with complicated network configurations.",[15,36208,36209],{},"Simply choose the target instance by name, and FlowFuse handles the connection automatically. This streamlines the management of multi-instance environments, ensuring smooth communication between flows across different devices or locations. Whether you're handling multiple projects or managing large-scale systems, FlowFuse Project Nodes help you save time and minimize errors.",[15,36211,36212,36213,29],{},"FlowFuse continues to push the boundaries of collaboration and scalability in Node-RED projects. For more details on these features, visit the ",[63,36214,36215],{"href":810},"FlowFuse website",[2582,36217,36218],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":103,"searchDepth":104,"depth":104,"links":36220},[36221,36222,36223,36224],{"id":31384,"depth":294,"text":31385},{"id":36115,"depth":294,"text":36116},{"id":36156,"depth":294,"text":36157},{"id":36198,"depth":294,"text":36199},"2023-03-13",{"excerpt":36227},{"type":12,"value":36228},[36229],[15,36230,28944],{},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-4",{"title":31173,"description":30352},{"loc":36231},"blog\u002F2023\u002F03\u002F3-quick-node-red-tips-4",[8650,138,36236],"tips","fV7mgUEcO2-eiDHigRZYjGDRmvkvNDh7VLYutkskpnk",{"id":36239,"title":36240,"authors":36241,"body":36242,"cta":3,"date":36225,"description":36714,"extension":116,"image":36715,"lastUpdated":6064,"meta":36716,"navigation":129,"path":36722,"seo":36723,"sitemap":36724,"stem":36725,"subtitle":36726,"tags":36727,"tldr":3,"video":3,"__hash__":36728},"blog\u002Fblog\u002F2023\u002F03\u002Fcomparing-node-red-dashboards.md","Comparing Node-RED Dashboards Solutions",[19494],{"type":12,"value":36243,"toc":36677},[36244,36247,36266,36270,36287,36290,36309,36313,36316,36338,36341,36344,36346,36350,36353,36357,36360,36364,36376,36379,36382,36391,36400,36403,36406,36410,36413,36417,36420,36424,36427,36430,36433,36436,36440,36443,36447,36450,36452,36456,36459,36462,36474,36479,36482,36486,36489,36493,36500,36503,36506,36509,36512,36518,36521,36524,36530,36533,36536,36542,36548,36551,36553,36557,36563,36568,36577,36583,36587,36593,36598,36601,36605,36619,36626,36634,36644,36658,36661,36665,36668,36671,36674],[15,36245,36246],{},"Dashboards are a great feature of Node-RED, allowing you to easily expose data visualisations and interactive elements of your flows to users via a web browser. I often see discussions in the community about which dashboard option is best for any given scenario, I wanted to compare the most popular options as they stand in early 2023.",[17286,36248,29314,36250,29314,36255],{"className":36249},[29313],[15,36251,36252,36254],{},[19,36253,29319],{}," Since this article was published, it's worth noting a couple of important updates:",[39,36256,36257,36258,36257,36261,29314],{},"\n        ",[42,36259,36260],{},"FlexDash is no longer maintained and supported.",[42,36262,36263,36265],{},[63,36264,643],{"href":641}," has been released, which is a new, modern dashboard stack for Node-RED, and offers all of the benefits of the original \"Node-RED Dashboard\", plus more.",[31,36267,36269],{"id":36268},"which-dashboards-am-i-going-to-consider","Which dashboards am I going to consider?",[15,36271,36272,36273,853,36277,4972,36282,29],{},"Based on their downloads per week and active development, I believe there are 3 main dashboards worth considering. In no particular order, they are ",[63,36274,36276],{"href":11860,"rel":36275},[67],"Dashboard",[63,36278,36281],{"href":36279,"rel":36280},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-uibuilder",[67],"uibuilder",[63,36283,36286],{"href":36284,"rel":36285},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002F@flexdash\u002Fnode-red-fd-corewidgets",[67],"FlexDash",[15,36288,36289],{},"It's not to say that there are not other options, I am focusing on the dashboards I believe are popular in the Node-RED community.",[15,36291,36292,36293,853,36298,4972,36303,36308],{},"I'd like to take this opportunity to thank the project leads for each of the three dashboards for responding to me and providing their take on the current state and future development of each. Where possible I have quoted their words, either from their messages to me or from the projects' documentation. Thanks to ",[63,36294,36297],{"href":36295,"rel":36296},"https:\u002F\u002Fgithub.com\u002Fdceejay",[67],"Dave",[63,36299,36302],{"href":36300,"rel":36301},"https:\u002F\u002Fgithub.com\u002FTotallyInformation",[67],"Julian",[63,36304,36307],{"href":36305,"rel":36306},"https:\u002F\u002Fgithub.com\u002Ftve",[67],"Thorsten"," for their replies as well as all the work they've put into these great projects!",[31,36310,36312],{"id":36311},"methodology","Methodology",[15,36314,36315],{},"To compare these dashboards, I am going to consider each of them based on the following factors:",[39,36317,36318,36321,36324,36327,36330,36333,36336],{},[42,36319,36320],{},"How easy is it to install?",[42,36322,36323],{},"How easy is it to get your first demo dashboard running?",[42,36325,36326],{},"How extensive is the collection of UI elements?",[42,36328,36329],{},"How good is the support and documentation?",[42,36331,36332],{},"How 'cloud native' is the dashboard?",[42,36334,36335],{},"How active is each project's development?",[42,36337,12176],{},[15,36339,36340],{},"I am assuming the user is a low-code developer. They may have limited experience with coding and are most comfortable working in visual interfaces.",[15,36342,36343],{},"So, that's the methodology, let's get on with looking at the strengths of each project.",[31,36345,36320],{"id":11813},[399,36347,36349],{"id":36348},"uibuilder-1st-place","uibuilder - 1st place",[15,36351,36352],{},"A search on Google for uibuilder returns the correct custom node. When searching for the custom node in the palette manager there is only one result, this is great as users are very likely to install what they were searching for. Once you've found the correct custom node, the installation takes just a few moments using the palette manager.",[399,36354,36356],{"id":36355},"dashboard-2nd-place","Dashboard - 2nd place",[15,36358,36359],{},"As Dashboard is currently the most popular solution to build dashboards in Node-RED, it's very easy to find both in search engines and in the Node-RED interface. A Google search brings up the correct custom node. Finding this custom node in Node-RED's palette manager is not quite as easy, at the time of writing it's the third from top result for the search term 'dashboard'. Some users might not select the intended item from the palette manager on first attempt. However, once you have found the correct custom node, installation is easy and takes just a few moments.",[399,36361,36363],{"id":36362},"flexdash-3rd-place","FlexDash - 3rd place",[15,36365,36366,36367],{},"When searching for 'FlexDash node red' on Google, the top result is the Node-RED website for the custom node. The issue with this, and this is also a problem when searching in the palette manager, is the project 'FlexDash' is apparently not what we actually need to install. When reading the readme for the project on Github it says ",[170,36368,36369,36370,36375],{},"'You most likely do not want to explicitly install this package, you want to install the ",[63,36371,36374],{"href":36372,"rel":36373},"https:\u002F\u002Fgithub.com\u002Fflexdash\u002Fnode-red-fd-corewidgets",[67],"core widgets",", which will bring in this package and more and will provide a usable whole'.",[15,36377,36378],{},"Credit to the developers for adding in this helpful text but I suspect most users will start off by installing FlexDash then later discover that was not the correct way to proceed. It would be great if the custom node which needs to be installed was the one called 'FlexDash' in my opinion.",[15,36380,36381],{},"This problem is compounded by there being no help file at all for 'FlexDash' showing up on the Node-RED web site. That may well be a deliberate attempt to help users get the right custom node installed, but it was still a confusing start for me and I suspect other users will have a similar experience.",[15,36383,36384,36385,36390],{},"When setting up FlexDash, one thing that wasn't immediately obvious was that I needed to restart Node-RED before the custom node showed in the palette. This step is ",[63,36386,36389],{"href":36387,"rel":36388},"https:\u002F\u002Fflexdash.github.io\u002Fdocs\u002Fquick-start\u002F#installing-flexdash-in-node-red",[67],"covered in the docs"," but I suspect a lot of users will get stuck working out why the palette manager says the custom node is installed but nothing new has been added to the palette.",[15,36392,36393,36394,36399],{},"There is also an ",[63,36395,36398],{"href":36396,"rel":36397},"https:\u002F\u002Fgithub.com\u002Fnode-red\u002Fnode-red\u002Fissues\u002F569",[67],"ongoing discussion"," about a way to resolve issue by changing how Node-RED deals with dependencies which sounds promising.",[15,36401,36402],{},"I believe a few improvements to the install process could make FlexDash a much more popular custom node.",[31,36404,36323],{"id":36405},"how-easy-is-it-to-get-your-first-demo-dashboard-running",[399,36407,36409],{"id":36408},"flexdash-1st-place","FlexDash - 1st place",[15,36411,36412],{},"Getting an example dashboard up and running in FlexDash is very easy thanks to the example flows which are included in the package. Simply go to 'Import', 'Examples' then select 'Hello-world' from the example flows. Now deploy and add \u002Fflexdash to the end of the URL of your Node-RED editor and you should have your first dashboard running.",[399,36414,36416],{"id":36415},"uibuilder-2nd-place","uibuilder - 2nd place",[15,36418,36419],{},"It was quite simple to get an example dashboard up and running in uibuilder. As with FlexDash, there are examples you can import. Once we import an example we do start to see the significantly different approach to delivering dashboards with uibuilder to the other two solutions. The examples seem to demonstrate how you could build a dashboard rather than showing specific UI elements such as charts in use.",[399,36421,36423],{"id":36422},"dashboard-3rd-place","Dashboard - 3rd place",[15,36425,36426],{},"Getting your first dashboard running in Dashboard is quite easy, once installed you need to drag in a Dashboard UI element then assign that to a UI group and tab. The group and tab can be left as their default options (home) which I suspect most users will work out quickly.",[15,36428,36429],{},"You then need to deploy your flow and visit the dashboard using '\u002Fui' on the end of the URL of your Node-RED editor and you are up and running.",[15,36431,36432],{},"Dashboard would benefit from some example flows as we see with the other two custom nodes.",[31,36434,36326],{"id":36435},"how-extensive-is-the-collection-of-ui-elements",[399,36437,36439],{"id":36438},"flexdash-and-dashboard-joint-1st-place","FlexDash and Dashboard - joint 1st place",[15,36441,36442],{},"It's really hard to separate these two, when considering the UI elements they come with. They both have out of the box solutions for charts, gauges, buttons, drop downs, toggles, text etc. I think they both deserve 1st place in this category.",[399,36444,36446],{"id":36445},"uibuilder-3rd-place","uibuilder - 3rd place",[15,36448,36449],{},"This is possibly a little unfair on uibuilder. Arguably by design, uibuilder does not currently include many UI elements. To add most useful elements (charts, gauges etc) to your dashboard, you will need to set out your design in HTML or look at using one of the supported frontend frameworks. This makes uibuilder more versatile for users who are comfortable using code to set out dashboards but for the low-coders among us it's less ideal.",[31,36451,36329],{"id":12159},[399,36453,36455],{"id":36454},"all-three-joint-first-place","All three - joint first place",[15,36457,36458],{},"All three projects have an active community and good support documentation. Where as I may have a personal preference about how I like documentation to be set out, I don't think that makes any one project better than the rest.",[31,36460,36332],{"id":36461},"how-cloud-native-is-the-dashboard",[15,36463,36464,36465,15373,36470,36473],{},"In the words of the ",[63,36466,36469],{"href":36467,"rel":36468},"https:\u002F\u002Fwww.cncf.io\u002F",[67],"Cloud Native Computing Foundation",[170,36471,36472],{},"Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach","'.",[15,36475,15363,36476,36473],{},[170,36477,36478],{},"These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil",[15,36480,36481],{},"So, how well does each project conform to these ideals?",[399,36483,36485],{"id":36484},"dashboard-1st-place","Dashboard - 1st place",[15,36487,36488],{},"Dashboard stores all configuration data within the Node-RED instance. When deploying an existing Node-RED project to a new instance everything just works exactly as it did previously.",[399,36490,36492],{"id":36491},"flexdash-2nd-place","FlexDash - 2nd place",[15,36494,36495,36496,36499],{},"As with Dashboard, everything required to define each dashboard is stored within Node-RED. This makes redeployment trivial. Unfortunately, as the Node-RED instance currently needs to be restarted before FlexDash works, it just missed out on joint first place. It would be great to see that issue resolved in future versions. There is also an ",[63,36497,36398],{"href":36396,"rel":36498},[67]," about a way to resolve issue by changing how Node-RED deals with dependencies.",[399,36501,36446],{"id":36502},"uibuilder-3rd-place-1",[15,36504,36505],{},"uibuilder uses the filesystem of the host instance to store its configuration. In practice this means that if you migrate the Node-RED project files to a new location you probably will find your dashboard no longer works. This can be mitigated by also migrating the filesystem (for example using persistent storage in Docker) and re-deploying via a Docker registry but it would be great to see uibuilder move towards not being dependant on the filesystem as it will make DevOps tasks that much easier.",[31,36507,36335],{"id":36508},"how-active-is-each-projects-development",[399,36510,36349],{"id":36511},"uibuilder-1st-place-1",[15,36513,36514],{},[165,36515],{"alt":36516,"src":36517,"title":36516},"Image showing the uibuilder Github Commits","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fuibuilder-activity.png",[15,36519,36520],{},"uibuilder has has consistent commits to the project going back several years with even greater activity since the start of 2022.",[399,36522,36492],{"id":36523},"flexdash-2nd-place-1",[15,36525,36526],{},[165,36527],{"alt":36528,"src":36529,"title":36528},"Image showing the FlexDash Github Commits","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fflexdash-activity.png",[15,36531,36532],{},"The commits to FlexDash have been regular since mid 2022.",[399,36534,36423],{"id":36535},"dashboard-3rd-place-1",[15,36537,36538],{},[165,36539],{"alt":36540,"src":36541,"title":36540},"Image showing the Dashboard Github Commits","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fdashboard-activity.png",[15,36543,36544,36545,29],{},"Dashboard is now in a a maintenance only state. In the words of the project lead, ",[170,36546,36547],{},"'Angular 1 (the framework used to build Dashboard) is now unsupported and it's just a matter of time before there is a serious security hole raised against it, for which there will be no fix. Of course we don't use all the features of it so we may be lucky that an exploit doesn't necessarily expose us directly but it will compromise any audits people may wish to do'",[15,36549,36550],{},"In practice, this means that sooner or later using Dashboard might become a significant security risk.",[31,36552,12176],{"id":12175},[399,36554,36556],{"id":36555},"uibuilder-joint-firstplace","uibuilder - joint firstplace",[15,36558,36559,36560,29],{},"During the writing of this article, uibuilder released a new version with some significant new features. In the words of the project lead when talking about version 6.1.0, '",[170,36561,36562],{},"It feels like uibuilder really is growing up. No more apologising for not being a direct Node-RED Dashboard replacement, uibuilder has its own path",[15,36564,36565,36473],{},[170,36566,36567],{},"You can now create and update visible web page elements direct from Node-RED data without needing to understand all of the intricacies and inconsistencies of HTML. You can create your own utility tools either in Node-RED or in front-end code that leverages the low-code UI features of uibuilder",[15,36569,36570,36571,36576],{},"It's great to see projects under active development. My greatest difficulty when using uibuilder, is I found it hard to create the UI elements I needed based on low-code workflows. According to the ",[63,36572,36575],{"href":36573,"rel":36574},"https:\u002F\u002Ftotallyinformation.github.io\u002Fnode-red-contrib-uibuilder\u002F#\u002Froadmap",[67],"development roadmap for uibuilder"," the project should progress towards being easier and easier for low-coders to make use of.",[15,36578,36579,36580,36473],{},"To again quote the project lead, '",[170,36581,36582],{},"In general, the ongoing direction of travel is to enable more zero-code features that will work both in Node-RED flows and in front-end custom code. The low-code feature set is already quite mature now and is well documented enough that it can be used by other tools should anyone wish to do so. I will be making sure that both the zero-code and low-code features are as easy to use as possible both from Node-RED and front-end code for maximum flexibility",[399,36584,36586],{"id":36585},"flexdash-joint-firstplace","FlexDash - joint firstplace",[15,36588,36589,36590,36473],{},"The project Lead for FlexDash had the following to say about the future development of the project. '",[170,36591,36592],{},"FlexDash currently has a fairly rigid overall page structure: there's a tab bar and each tab's content is organized in grids of widgets. The plan is to open this up fully so the user can start from a blank page and place containers which contain widgets. This way almost any layout could be implemented in FlexDash",[15,36594,15363,36595,36473],{},[170,36596,36597],{},"I also would like to improve the multi-user capabilities of FlexDash by supporting authentication and making it easier for users to implement flows that present per-user data in the dashboard",[15,36599,36600],{},"After using FlexDash over the past couple of weeks and finding it to be already be a strong contender for all my Node-RED dashboard needs, it's great to see it continuing to be improved.",[399,36602,36604],{"id":36603},"dashboard-third-place","Dashboard - third place",[15,36606,36607,69,36612],{},[170,36608,36609],{},[19,36610,36611],{},"Important Update: New Generation of Node-RED Dashboard Released:",[170,36613,36614,36615,36618],{},"A new generation of the outdated and unmaintained Node-RED Dashboard has been released to replace it. Introducing ",[63,36616,643],{"href":3463,"rel":36617},[67],", built on Vue.js, offering significantly more versatility than its predecessor. This new dashboard, managed by FlowFuse, is designed to allow full customization, addressing the limitations of the previous version.",[15,36620,36621,36622,29],{},"*Node-RED Dashboard 2.0 retains most of the widgets and concepts from the old version, so transitioning from Node-RED Dashboard 1.0 to 2.0 is easy. For a full feature comparison you can check out the ",[63,36623,13997],{"href":36624,"rel":36625},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fuser\u002Fmigration.html",[67],[15,36627,36628,36629,36633],{},"Dashboard 2.0 includes various chart types such as line, scatter, bar, gauge, and more, and it's compatible with all the ",[63,36630,36632],{"href":13857,"rel":36631},[67],"Vuetify component library",", making it easier to build advanced dashboards.*",[15,36635,36636],{},[170,36637,36638,36639,18684],{},"Furthermore, the team is continuously working on adding more amazing features to enhance the user experience. For a smooth transition, FlowFuse provides easy-to-follow guides. Refer to ",[63,36640,36643],{"href":36641,"rel":36642},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002Fdashboard\u002F",[67],"Node-RED Dashboard 2.0 Guides",[15,36645,36646,36647,36652,36653,29],{},"As mentioned above, Dashboard is no longer in active development. This is due to the framework upon which it was build ",[63,36648,36651],{"href":36649,"rel":36650},"https:\u002F\u002Fangularjs.org\u002F",[67],"(AngularJS)"," now being unsupported as of the end of 2021. You can read a lot more detail on why ongoing development of Dashboard is not practical in this ",[63,36654,36657],{"href":36655,"rel":36656},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fdiscussion-about-a-new-dashboard\u002F51119\u002F3",[67],"thread on the Node-RED forums",[15,36659,36660],{},"There could possibly be new effort put into porting Dashboard over to a new framework but that is a significant amount of work. I suspect it would take hundreds of hours development just to get the feature set back to the same state as the current version so I suspect it won't ever happen.",[31,36662,36664],{"id":36663},"conclusions","Conclusions",[15,36666,36667],{},"Personally, I was a little surprised by these results. I have used Dashboard for around 3 years and always found it to be a great tool for putting together quick and informative dashboards. That being said, when attempting to objectively compare it to uibuilder and FlexDash, the other two projects often are individually better in a given category. That coupled with the halt of development for Dashboard due to AngularJS being no longer supported, it's hard to recommend Dashboard for totally new Node-RED users in 2023, especially for commercial projects.",[15,36669,36670],{},"If you already use Dashboard, in a non-commercial setting you should probably continue to do so, you might find that its development slows down to a near stall due to the underlying framework now being abandoned but for at least as of right now it's a great solution to build your Node-RED dashboards in.",[15,36672,36673],{},"FlexDash is probably the best low-code solution for building dashboards in Node-RED. If you don't get blocked by the confusing install process I believe it's the one to pick up at the time of writing due to it's ongoing support and low-code interface.",[15,36675,36676],{},"uibuilder is currently not what I would consider a truly low-code option for creating dashboards but it is moving in that direction. It has some great features and is extremely flexible so it has a good chance of ending up as the most popular solution to build dashboards in Node-RED in the long term. That being said, as of time of writing unless you are a 'coder' you will may struggle to build a dashboard using it.",{"title":103,"searchDepth":104,"depth":104,"links":36678},[36679,36680,36681,36686,36691,36695,36698,36703,36708,36713],{"id":36268,"depth":107,"text":36269},{"id":36311,"depth":107,"text":36312},{"id":11813,"depth":107,"text":36320,"children":36682},[36683,36684,36685],{"id":36348,"depth":294,"text":36349},{"id":36355,"depth":294,"text":36356},{"id":36362,"depth":294,"text":36363},{"id":36405,"depth":107,"text":36323,"children":36687},[36688,36689,36690],{"id":36408,"depth":294,"text":36409},{"id":36415,"depth":294,"text":36416},{"id":36422,"depth":294,"text":36423},{"id":36435,"depth":107,"text":36326,"children":36692},[36693,36694],{"id":36438,"depth":294,"text":36439},{"id":36445,"depth":294,"text":36446},{"id":12159,"depth":107,"text":36329,"children":36696},[36697],{"id":36454,"depth":294,"text":36455},{"id":36461,"depth":107,"text":36332,"children":36699},[36700,36701,36702],{"id":36484,"depth":294,"text":36485},{"id":36491,"depth":294,"text":36492},{"id":36502,"depth":294,"text":36446},{"id":36508,"depth":107,"text":36335,"children":36704},[36705,36706,36707],{"id":36511,"depth":294,"text":36349},{"id":36523,"depth":294,"text":36492},{"id":36535,"depth":294,"text":36423},{"id":12175,"depth":107,"text":12176,"children":36709},[36710,36711,36712],{"id":36555,"depth":294,"text":36556},{"id":36585,"depth":294,"text":36586},{"id":36603,"depth":294,"text":36604},{"id":36663,"depth":107,"text":36664},"Explore a thorough comparison of Node-RED dashboard solutions, covering installation, UI elements, support, and future development plans.","\u002Fimages\u002Fblog\u002Fdb-comparison.jpg",{"keywords":36717,"excerpt":36718},"node red dashboard, node-red dashboard, node red ui builder, ui builder node red, flexdash, node red dashboard alternative, node red alternative dashboard",{"type":12,"value":36719},[36720],[15,36721,36246],{},"\u002Fblog\u002F2023\u002F03\u002Fcomparing-node-red-dashboards",{"title":36240,"description":36714},{"loc":36722},"blog\u002F2023\u002F03\u002Fcomparing-node-red-dashboards","One of the most common features Node-RED users add to their flows is a dashboard, we compaure the 3 most popular options.",[3124,138,12631,12632],"WLaG1Z5dQtBGepVDycZbwzakm1DqWxvg4fKLicMACOk",{"id":36730,"title":29163,"authors":36731,"body":36732,"cta":3,"date":36822,"description":30352,"extension":116,"image":29183,"lastUpdated":6064,"meta":36823,"navigation":129,"path":36828,"seo":36829,"sitemap":36830,"stem":36831,"subtitle":30352,"tags":36832,"tldr":3,"video":3,"__hash__":36833},"blog\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-3.md",[19494],{"type":12,"value":36733,"toc":36816},[36734,36736,36740,36743,36750,36754,36757,36763,36766,36772,36776,36779,36785,36788,36794,36798,36802,36808,36811],[15,36735,28944],{},[399,36737,36739],{"id":36738},"_1-the-exec-node-allows-you-to-interact-with-bash-from-node-red","1. The Exec node allows you to interact with BASH from Node-RED",[15,36741,36742],{},"Exec allows you to run Shell commands and receive the value back into your flow. This opens up almost any command which can be run on the host devices CLI to your Node-RED flows.",[15,36744,36745],{},[165,36746],{"alt":36747,"src":36748,"title":36749},"\"Example flow using the Exec node\"","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fexec-example.gif","Example flow using the Exec node",[399,36751,36753],{"id":36752},"_2-the-filter-node-helps-you-discard-duplicate-messages","2. The Filter node helps you discard duplicate messages",[15,36755,36756],{},"It can be useful to only allow messages to proceed through a flow where their value is unique. Filter makes that task simple, no need to store the past values and check each new message against a list.",[15,36758,36759],{},[165,36760],{"alt":36761,"src":36762,"title":36761},"Configuring the Filter node to only allow unique payloads through","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Ffilter-config.png",[15,36764,36765],{},"Once your filter is configured as shown above, try sending different payloads through to see the outcome.",[15,36767,36768],{},[165,36769],{"alt":36770,"src":36771,"title":36770},"Demonstration showing the Filter node","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Ffilter-example.gif",[399,36773,36775],{"id":36774},"_3-counting-the-amount-of-messages-sent-to-a-debug-node","3. Counting the amount of messages sent to a Debug node",[15,36777,36778],{},"The Debug node has a lot of great features that we don't see used that often. One example is the ability to show a count of how many messages have been sent to that Debug node since the last deploy.",[15,36780,36781],{},[165,36782],{"alt":36783,"src":36784,"title":36783},"Setting up the debug to count messages","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fsetup-counting-debug.png",[15,36786,36787],{},"Once you've setup the node as shown above, you will see a counter under the debug.",[15,36789,36790],{},[165,36791],{"alt":36792,"src":36793,"title":36792},"Each message sent to the debug node is counted","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fcounting-debug.gif",[15,36795,29135,36796,29],{},[63,36797,12885],{"href":29138},[31,36799,36801],{"id":36800},"effortless-communication-between-node-red-instances-with-flowfuse-project-nodes","Effortless Communication Between Node-RED Instances with FlowFuse Project Nodes",[15,36803,36804,36805,36807],{},"Managing communication between multiple Node-RED instances can be a complex task, but FlowFuse ",[63,36806,36205],{"href":18479}," simplify this process dramatically. With these nodes, you can easily send messages between different Node-RED instances without worrying about complex configurations or network setup.",[15,36809,36810],{},"All you need to do is select the target instance by name, and FlowFuse takes care of the rest. This makes it faster and more efficient to handle multi-instance environments, ensuring seamless communication between flows across different devices or locations. Whether you're managing multiple environments or working on large-scale projects, FlowFuse Project Nodes save you time and reduce the risk of errors.",[15,36812,36813,36814,29],{},"FlowFuse continues to innovate, making collaboration and scalability in Node-RED projects even easier. To learn more about these features, check out the ",[63,36815,36215],{"href":810},{"title":103,"searchDepth":104,"depth":104,"links":36817},[36818,36819,36820,36821],{"id":36738,"depth":294,"text":36739},{"id":36752,"depth":294,"text":36753},{"id":36774,"depth":294,"text":36775},{"id":36800,"depth":107,"text":36801},"2023-03-07",{"excerpt":36824},{"type":12,"value":36825},[36826],[15,36827,28944],{},"\u002Fblog\u002F2023\u002F03\u002F3-quick-node-red-tips-3",{"title":29163,"description":30352},{"loc":36828},"blog\u002F2023\u002F03\u002F3-quick-node-red-tips-3",[8650,138,7457],"7KTqvDVUBaGvQdx41zIToO1NAGFSUjEyLTjqU8cz7Hk",{"id":36835,"title":36836,"authors":36837,"body":36838,"cta":3,"date":37100,"description":37101,"extension":116,"image":37102,"lastUpdated":3,"meta":37103,"navigation":129,"path":37108,"seo":37109,"sitemap":37110,"stem":37111,"subtitle":37112,"tags":37113,"tldr":3,"video":3,"__hash__":37114},"blog\u002Fblog\u002F2023\u002F03\u002Fintegration-platform-for-edge-computing.md","Node-RED: The Integration Platform for IIoT Edge Computing & PLCs",[29200],{"type":12,"value":36839,"toc":37093},[36840,36843,36847,36853,36856,36860,36885,36888,36892,36895,36898,36902,36905,36908,37074,37077,37088,37090],[15,36841,36842],{},"Node-RED has become a widely adopted integration platform for IoT edge computing and PLCs. Discover why!",[31,36844,36846],{"id":36845},"the-integration-platform-for-iiot-edge-computing-plcs","The Integration Platform for IIoT Edge Computing & PLCs",[15,36848,36849,36850,36852],{},"Node-RED is a widely adopted open-source low-code development tool that makes it easy to connect and integrate different sources of data. With a visual programming interface and drag-and-drop functionality, ",[63,36851,811],{"href":10415}," makes it possible for software developers and non-professional software developers to create sophisticated applications.",[15,36854,36855],{},"In the manufacturing and industrial automation industry, the focus of the Industrial Internet of Things (IIoT) has been on integrating industrial processes and equipment to enable real-time monitoring, control, and analysis of data. For many use cases, instead of sending all the data to the cloud, the best practice for processing industrial data is to deploy the application to the edge of the network, referred to as edge computing. By processing the data closer to the data source, edge computing has many benefits, including reduced latency, limited downtime, conserving bandwidth, and increased privacy, and security.",[31,36857,36859],{"id":36858},"how-node-red-fits-into-iiot-edge-computing","How Node-RED fits into IIoT Edge Computing",[15,36861,36862,36863,36868,36869,853,36873,853,36876,853,36880,36884],{},"A key challenge for IIoT edge computing is the wide variety of different hardware platforms, protocols and sources of data and processes. Over the years, the Node-RED community has built ",[63,36864,36867],{"href":36865,"rel":36866},"https:\u002F\u002Fflows.nodered.org\u002F",[67],"thousands of nodes and flow","s to support a wider range of these sources of data, including support for ",[63,36870,2646],{"href":36871,"rel":36872},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-modbus",[67],[63,36874,2641],{"href":11366,"rel":36875},[67],[63,36877,36879],{"href":7073,"rel":36878},[67],"S7",[63,36881,1400],{"href":36882,"rel":36883},"https:\u002F\u002Fcookbook.nodered.org\u002Fmqtt\u002F",[67],", etc. Node-RED also has nodes for graphics and dashboards to make it trivial to visualize industrial data.",[15,36886,36887],{},"Node-RED’s visual programming environment makes it accessible to non-developers. Manufacturing, mechanical, and electrical engineers in the factories are typically the domain experts in understanding the existing systems and often lead IIoT initiatives. Node-RED enables these engineers to quickly innovate and create real value for their organizations. This makes it a popular choice for engineers looking to create edge computing solutions.",[31,36889,36891],{"id":36890},"plc-and-iot-gateway-vendors-embrace-node-red","PLC and IoT Gateway Vendors Embrace Node-RED",[15,36893,36894],{},"PLC and IoT Gateway vendors are at the forefront of promoting edge computing. They see edge computing as a way to modernize hardware in the factory and for remote asset management. PLC and IoT gateways often sit in front of old legacy systems that don’t have the connectivity or compute platform to enable IIoT applications.",[15,36896,36897],{},"Many of these hardware vendors realized they need an application delivery platform for their devices. Traditional OT hardware vendors often implement proprietary software stacks that are often difficult to use and closed to integrating with other hardware and software. Forward thinking hardware vendors realized having an open platform is the future of their industry and customers have begun to demand more open platforms. Node-RED’s ease of use, open community and open source license provided the solution many of these hardware vendors were looking for.",[31,36899,36901],{"id":36900},"the-standard-for-edge-computing-and-plcs","The Standard for Edge Computing and PLCs",[15,36903,36904],{},"Today, Node-RED has been adopted by some of the leading PLC and IoT Gateway vendors. The hardware vendor community appears to have standardized on Node-RED as being the edge computing platform for IIoT.",[15,36906,36907],{},"Below is a sample of the vendors offering a Node-RED solution:",[271,36909,36910,36918,36926,36934,36942,36949,36957,36965,36972,36980,36988,36996,37004,37011,37019,37027,37035,37043,37050,37058,37066],{},[42,36911,36912,36917],{},[63,36913,36916],{"href":36914,"rel":36915},"https:\u002F\u002Fwww.advantech.com\u002Fen-eu\u002Fproducts\u002Fnode-red-gateways\u002Fsub_fb7246cc-cc10-486f-806b-30bb50a90f28",[67],"Advantech"," Node-RED Field Gateway",[42,36919,36920,36925],{},[63,36921,36924],{"href":36922,"rel":36923},"https:\u002F\u002Finfosys.beckhoff.com\u002Fenglish.php?content=..\u002Fcontent\u002F1033\u002Ftf6720_tc3_iot_data_agent\u002F3260672139.html&id=",[67],"Bechhoff"," TwinCAT",[42,36927,36928,36933],{},[63,36929,36932],{"href":36930,"rel":36931},"https:\u002F\u002Fwww.bivocom.com\u002Fproducts\u002Fiot-gateways\u002Fedge-iot-gateway-tg452",[67],"Bivocom"," TG452 IoT Edge Gateway",[42,36935,36936,36941],{},[63,36937,36940],{"href":36938,"rel":36939},"https:\u002F\u002Fbliiot.com\u002Fproducts\u002F",[67],"BLIIOT Edge Computing Gateway"," EdgeCom BL302",[42,36943,36944],{},[63,36945,36948],{"href":36946,"rel":36947},"https:\u002F\u002Fdeveloper.community.boschrexroth.com\u002Ft5\u002FStore-and-How-to\u002FctrlX-CORE-Node-RED-App\u002Fba-p\u002F22366",[67],"Bosch CtrlX Core",[42,36950,36951,36956],{},[63,36952,36955],{"href":36953,"rel":36954},"https:\u002F\u002Fwww.broadsens.com\u002Fwireless-gateway\u002F",[67],"Broadsens"," GU200 & GU 200S",[42,36958,36959,36964],{},[63,36960,36963],{"href":36961,"rel":36962},"https:\u002F\u002Fwww.emerson.com\u002Fdocuments\u002Fautomation\u002Fproduct-datasheet-pacedge-software-computing-devices-pacsystems-en-7205588.pdf",[67],"Emerson"," PACEdge",[42,36966,36967],{},[63,36968,36971],{"href":36969,"rel":36970},"https:\u002F\u002Fgithub.com\u002FHilscherAutomation\u002FnetPI-nodered",[67],"Hilscher Automation",[42,36973,36974,36979],{},[63,36975,36978],{"href":36976,"rel":36977},"https:\u002F\u002Fdeveloper.opto22.com\u002Fnodered\u002Fgeneral\u002F",[67],"Opto22"," groov RIO & EPIC",[42,36981,36982,36987],{},[63,36983,36986],{"href":36984,"rel":36985},"https:\u002F\u002Fwww.parallaxcontrol.com\u002F",[67],"Parallax AV"," Control System",[42,36989,36990,36995],{},[63,36991,36994],{"href":36992,"rel":36993},"https:\u002F\u002Fdocs.particle.io\u002Freference\u002Fcloud-apis\u002Fnode-red\u002F",[67],"Particle.io"," Particle",[42,36997,36998,37003],{},[63,36999,37002],{"href":37000,"rel":37001},"https:\u002F\u002Fwww.pepperl-fuchs.com\u002Fusa\u002Fen\u002Fclassid_199.htm?view=productdetails&prodid=93839",[67],"Pepperl+Fuchs"," AS-Interface gateway",[42,37005,37006],{},[63,37007,37010],{"href":37008,"rel":37009},"https:\u002F\u002Fprojects.raspberrypi.org\u002Fen\u002Fprojects\u002Fgetting-started-with-node-red",[67],"Raspberry Pi",[42,37012,37013,37018],{},[63,37014,37017],{"href":37015,"rel":37016},"https:\u002F\u002Fwww.renesas.com\u002Fus\u002Fen\u002Fproducts\u002Fprogrammable-mixed-signal-asic-ip-products\u002Fmixed-signal-asics\u002Fcommunication-asics\u002Fftclick-mikrobus-compatible-interface-module",[67],"Renesas"," FT Click",[42,37020,37021,37026],{},[63,37022,37025],{"href":37023,"rel":37024},"https:\u002F\u002Frevolutionpi.com\u002Frevpi-connect\u002F",[67],"Revolution Pi"," RevPi Connect",[42,37028,37029,37034],{},[63,37030,37033],{"href":37031,"rel":37032},"https:\u002F\u002Fshop.exchange.se.com\u002Fen-US\u002Fapps\u002F59823\u002Fecostruxure-plant-data-expert\u002Ffeatures",[67],"Schneider Electric"," ExoStructure Plant Data Expert",[42,37036,37037,37042],{},[63,37038,37041],{"href":37039,"rel":37040},"https:\u002F\u002Fgithub.com\u002FSIMATICmeetsLinux\u002FIOT2050-NodeRed-OPCUA-Server",[67],"Siemens"," S7 PLC",[42,37044,37045],{},[63,37046,37049],{"href":37047,"rel":37048},"https:\u002F\u002Fst-one.io\u002Fen\u002F",[67],"ST-One",[42,37051,37052,37057],{},[63,37053,37056],{"href":37054,"rel":37055},"https:\u002F\u002Fsupport.tulip.co\u002Fdocs\u002Fusing-node-red-with-edge-mc",[67],"Tulip"," Edge MC & Edge IO",[42,37059,37060,37065],{},[63,37061,37064],{"href":37062,"rel":37063},"https:\u002F\u002Fwww.wago.com\u002Fus\u002Fedge-devices-overview",[67],"Wago"," Edge Controller & Computer",[42,37067,37068,37073],{},[63,37069,37072],{"href":37070,"rel":37071},"https:\u002F\u002Fcatalog.weidmueller.com\u002Fprocat\u002FGroup.jsp;jsessionid=C885C404E7B4B798B23B8A9BB2200513?groupId=(%22group14048963834797%22)&page=Group",[67],"Weidmueller"," control web",[15,37075,37076],{},"There are several key reason Node-RED is so popular for IIoT edge computing, including:",[39,37078,37079,37082,37085],{},[42,37080,37081],{},"Easy User-friendly interface that makes it accessible to manufacturing engineers that might not have a lot of programming experience.",[42,37083,37084],{},"Large community of open source nodes that integrate with many different OT hardware and protocols.",[42,37086,37087],{},"Open source community and license making it vendor neutral so competing hardware vendors feel comfortable embracing the platform.",[31,37089,1425],{"id":1424},[15,37091,37092],{},"IIoT and edge computing is making software more critical to the manufacturing industry. The flexibility to integrate data from different sources to create innovative data centric solutions is primarily software driven. In partnership with OT hardware vendors, Node-RED’s flexible and easy to use environment provides the platform for manufacturing companies to embrace software to develop IIoT solutions.",{"title":103,"searchDepth":104,"depth":104,"links":37094},[37095,37096,37097,37098,37099],{"id":36845,"depth":107,"text":36846},{"id":36858,"depth":107,"text":36859},{"id":36890,"depth":107,"text":36891},{"id":36900,"depth":107,"text":36901},{"id":1424,"depth":107,"text":1425},"2023-03-06","Discover why Node-RED is the go-to integration platform for IIoT edge computing and PLCs, embraced by leading vendors for its versatility and ease of use.","\u002Fblog\u002F2023\u002F03\u002Fimages\u002Fintegration-platform-blog-image.jpg",{"excerpt":37104},{"type":12,"value":37105},[37106],[15,37107,36842],{},"\u002Fblog\u002F2023\u002F03\u002Fintegration-platform-for-edge-computing",{"title":36836,"description":37101},{"loc":37108},"blog\u002F2023\u002F03\u002Fintegration-platform-for-edge-computing","Node-RED's Role in IIoT Edge Computing & PLC Integration",[8650,138,137],"By2G9HrVRsuj38ghjWEETqIH_PhXVxr6VguIPDDeht0",{"id":37116,"title":37117,"authors":37118,"body":37119,"cta":3,"date":37223,"description":37224,"extension":116,"image":3,"lastUpdated":3,"meta":37225,"navigation":129,"path":37233,"seo":37234,"sitemap":37235,"stem":37236,"subtitle":37224,"tags":37237,"tldr":3,"video":3,"__hash__":37238},"blog\u002Fblog\u002F2023\u002F02\u002Fwebinar-1-missed-questions.md","Some questions we didn't get to in our first webinar",[19494],{"type":12,"value":37120,"toc":37217},[37121,37130,37134,37137,37146,37152,37155,37159,37162,37176,37180,37183,37190,37193,37196,37200,37203,37210],[15,37122,37123,37124,37129],{},"There were some great questions from our ",[63,37125,37128],{"href":37126,"rel":37127},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=47EvfmJji-k",[67],"first webinar"," that we didn't get time to answer, we wanted to share those questions and our answers here.",[399,37131,37133],{"id":37132},"irvin-asks-is-it-possible-to-save-the-debug-information-or-data-flow-to-a-storage-like-splunk-or-sql","Irvin asks, 'is it possible to save the debug information or data flow to a storage like Splunk or SQL'?",[15,37135,37136],{},"Hi Irvin, thanks for the question. I suspect you might be better using a custom node which is designed for logging data rather than capturing the debug node content to a database.",[15,37138,37139,37140,37145],{},"I've come across ",[63,37141,37144],{"href":37142,"rel":37143},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-flogger",[67],"Flogger"," which seems to do a good job of logging, including support for multiple log files, and built in support for log rotation.",[15,37147,37148],{},[165,37149],{"alt":37150,"src":37151,"title":37150},"Capturing debug to a log file using Flogger","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fflogger.gif",[15,37153,37154],{},"If you really wanted to log to a database rather than a log file you could create your own logging subflow. Once that's in place you can drop it into your flow as needed to capture your debug data for later consumption.",[399,37156,37158],{"id":37157},"anonymous-asks-can-we-make-an-mobile-application-with-node-red-or-can-the-content-only-be-accessed-through-a-web-browser","Anonymous asks 'can we make an mobile application with Node-RED or can the content only be accessed through a web browser'?",[15,37160,37161],{},"Hello Anon', it would be great if a Node-RED flow could be built into a mobile app. Sadly, there isn't a simple way to do so at the time of writing.",[15,37163,37164,37165,1413,37170,37175],{},"Assuming you want an easy way to package up and distribute the functionality you might be best creating a link to where the application is hosted. Both ",[63,37166,37169],{"href":37167,"rel":37168},"https:\u002F\u002Fwww.macrumors.com\u002Fhow-to\u002Fadd-a-web-link-to-home-screen-iphone-ipad\u002F",[67],"iOS",[63,37171,37174],{"href":37172,"rel":37173},"https:\u002F\u002Fwww.androidauthority.com\u002Fadd-website-android-iphone-home-screen-3181682\u002F",[67],"Android"," support making a home icon. Once added using them is basically the same user experience as a locally installed application.",[399,37177,37179],{"id":37178},"john-asks-from-the-random-node-example-in-the-webinar-the-node-connected-to-four-different-nodes-is-there-an-order-to-which-is-invoked-first-can-that-be-controlled","John asks 'From the random node example in the webinar, the node connected to four different nodes. Is there an order to which is invoked first? Can that be controlled'?",[15,37181,37182],{},"Hi John, interesting question, thanks for sending it in. For the sake of any readers who were not an the webinar here is what you are describing.",[15,37184,37185],{},[165,37186],{"alt":37187,"src":37188,"title":37189},"\"Image showing the Flow where the random number generator sends a message to 4 nodes at the same time\"","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fchart-flow.png","Image showing the Flow where the random number generator sends a message to 4 nodes at the same time",[15,37191,37192],{},"All downstream nodes linked to the same prior node will be triggered at practically the same time.",[15,37194,37195],{},"You could use a delay node if you want to ensure a particular node is triggered first. It might also make sense to wire your flow in series rather than parallel. This would allow your functions to all execute in a specific order. That being said, in this case all but one of the nodes do not have outputs so using delays might be the only practical option.",[399,37197,37199],{"id":37198},"abdelhamid-asks-how-can-i-delete-a-subflow","Abdelhamid asks, 'How can I delete a subflow'?",[15,37201,37202],{},"Thanks Abdelhamid, that's actually really easy to do. Double click the subflow you want to delete, then select 'delete subflow' from the top of your workspace.",[15,37204,37205],{},[165,37206],{"alt":37207,"src":37208,"title":37209},"\"Image showing how to delete a subflow\"","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fdelete-subflow.png","Image showing how to delete a subflow",[15,37211,37212,37213,29],{},"Thanks again to everyone who attended and participated in our first webinar. We have lots of other useful live content coming up soon, you can view and register for future events on our website's ",[63,37214,37216],{"href":37215},"\u002Fwebinars\u002F","webinars page",{"title":103,"searchDepth":104,"depth":104,"links":37218},[37219,37220,37221,37222],{"id":37132,"depth":294,"text":37133},{"id":37157,"depth":294,"text":37158},{"id":37178,"depth":294,"text":37179},{"id":37198,"depth":294,"text":37199},"2023-02-27","There were some great questions from our first webinar that we didn't get time to answer, we wanted to share those questions and our answers here.",{"excerpt":37226},{"type":12,"value":37227},[37228],[15,37229,37123,37230,37129],{},[63,37231,37128],{"href":37126,"rel":37232},[67],"\u002Fblog\u002F2023\u002F02\u002Fwebinar-1-missed-questions",{"title":37117,"description":37224},{"loc":37233},"blog\u002F2023\u002F02\u002Fwebinar-1-missed-questions",[138,137],"fq4wddftbitUe09C7OdBK5K1AU1F7aS8UilZ_stmfi0",{"id":37240,"title":29169,"authors":37241,"body":37242,"cta":3,"date":37326,"description":30352,"extension":116,"image":29183,"lastUpdated":6064,"meta":37327,"navigation":129,"path":37332,"seo":37333,"sitemap":37334,"stem":37335,"subtitle":30352,"tags":37336,"tldr":3,"video":3,"__hash__":37337},"blog\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-2.md",[19494],{"type":12,"value":37243,"toc":37320},[37244,37246,37250,37253,37256,37262,37266,37269,37275,37279,37282,37285,37288,37294,37298,37302,37312,37315],[15,37245,28944],{},[399,37247,37249],{"id":37248},"_1-deploy-just-what-youve-changed","1. Deploy just what you've changed",[15,37251,37252],{},"When deploying your changes, the default option is deploy everything which also restarts all your flows. You can also select to deploy just the nodes you edited or just the flows in which any changes were made.",[15,37254,37255],{},"This allows you to update part of your flow without restarting other sections. This can be really handy when you have different flows spread across your workspace or tabs but you don't want to reload them all each time you deploy.",[15,37257,37258],{},[165,37259],{"alt":37260,"src":37261,"title":37260},"Deploying only the changed nodes","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fdeploy.gif",[399,37263,37265],{"id":37264},"_2-find-which-debug-node-generated-an-entry-in-the-log","2. Find which debug node generated an entry in the log",[15,37267,37268],{},"Once your flow has a few debug nodes it can become challenging to see which particular node generated an entry in the log. To quickly track an entry back to its source, click the text 'node: debug' and you will be whisked back to the specific debug node, even it it's elsewhere on you workspace or even on a different tab.",[15,37270,37271],{},[165,37272],{"alt":37273,"src":37274,"title":37273},"Finding the debug node which generated the log line","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fdebug-jump.gif",[399,37276,37278],{"id":37277},"_3-the-delay-node-can-be-used-as-a-rate-limiter","3. The delay node can be used as a rate limiter",[15,37280,37281],{},"Sometimes it's useful to be able to limit messages to only allow one every so many minutes. You may for example send alerts when a temperature sensor goes above a particular threshold but you don't want your email or instant messaging inbox being swamped with repeated alerts for the same issue.",[15,37283,37284],{},"You can use the delay node to limit how many messages can pass through in a given period of time.",[15,37286,37287],{},"Open the delay node settings, select Rate Limit then select 1 message per 15 minutes, then select 'Drop intermediate messages'. This flow will now output a maximum of one message every quarter of an hour, all others will be deleted.",[15,37289,37290],{},[165,37291],{"alt":37292,"src":37293,"title":37292},"Limiting how many alerts are sent","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Frate-limit.gif",[15,37295,29135,37296,29],{},[63,37297,12885],{"href":29138},[31,37299,37301],{"id":37300},"enhance-efficiency-with-flowfuse","Enhance Efficiency with FlowFuse",[15,37303,37304,37305,37308,37309,37311],{},"FlowFuse is a cloud-based platform designed to boost collaboration, security, and scalability for your Node-RED applications. It features ",[63,37306,37307],{"href":23951},"numerous tools and functionalities"," that streamline the development-to-deployment process, including one-click deployment, the ",[63,37310,30129],{"href":30128},", and other capabilities that simplify managing your Node-RED environment.",[15,37313,37314],{},"For more tips, tricks, and professional development techniques with Node-RED, check out our recommended eBook:",[15,37316,37317],{},[63,37318,37319],{"href":25123},"The Ultimate Beginner's Guide to Professional Node-RED",{"title":103,"searchDepth":104,"depth":104,"links":37321},[37322,37323,37324,37325],{"id":37248,"depth":294,"text":37249},{"id":37264,"depth":294,"text":37265},{"id":37277,"depth":294,"text":37278},{"id":37300,"depth":107,"text":37301},"2023-02-23",{"excerpt":37328},{"type":12,"value":37329},[37330],[15,37331,28944],{},"\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-2",{"title":29169,"description":30352},{"loc":37332},"blog\u002F2023\u002F02\u002F3-quick-node-red-tips-2",[8650,138,36236],"pJFKqFyknKc76TXUwRcBdbndiMZjAqLKoDX2s_VZ8gs",{"id":37339,"title":37340,"authors":37341,"body":37342,"cta":3,"date":37326,"description":37455,"extension":116,"image":37456,"lastUpdated":6064,"meta":37457,"navigation":129,"path":37468,"seo":37469,"sitemap":37470,"stem":37471,"subtitle":37472,"tags":37473,"tldr":3,"video":3,"__hash__":37474},"blog\u002Fblog\u002F2023\u002F02\u002Fming-blog.md","MING Stack for IoT",[29200],{"type":12,"value":37343,"toc":37452},[37344,37359,37368,37371,37402,37405,37408,37422,37425,37428,37432,37435,37447],[15,37345,37346,37347,37352,37353,37358],{},"The folks at Balena have created a bundle they call the ",[63,37348,37351],{"href":37349,"rel":37350},"https:\u002F\u002Fhub.balena.io\u002Forganizations\u002Fmarc6\u002Fapps\u002FMING",[67],"MING stack",", (Mosquitto\u002FMQTT, InfluxDB, Node-RED and Grafana) which first appeared back in ",[63,37354,37357],{"href":37355,"rel":37356},"https:\u002F\u002Fforums.balena.io\u002Ft\u002Fming-an-iot-sensor-stack-mosquitto-influxdb-nodered-grafana\u002F36540",[67],"2019",". It is an interesting way to look at the IoT tech stack and a pattern I have seen used many times.",[15,37360,37361,37362,37367],{},"In the early days of the web, the ",[63,37363,37366],{"href":37364,"rel":37365},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLAMP_(software_bundle)",[67],"LAMP stack"," was popularized as being the open source tech stack for hosting web sites. First used in 1998, LAMP stood for Linux, Apache, MySQL and Python\u002FPerl\u002FPHP. The LAMP stack did a lot to popularize open source software and create architecture patterns for building web applications. The LAMP stack did a lot to simplify a confusing technology landscape back in the early days of the web.",[15,37369,37370],{},"Can the IoT industry benefit from a MING stack? There is a fair amount of complexity building IoT systems. Therefore, having defined architecture patterns might help reduce some of the confusion. In fact, MING does bring together the key open source components of an IoT system:",[39,37372,37373,37381,37388,37393],{},[42,37374,37375,37380],{},[63,37376,37379],{"href":37377,"rel":37378},"https:\u002F\u002Fmosquitto.org\u002F",[67],"Mosquitto"," is the popular open source MQTT broker. MQTT has become the default protocol for IoT communications. The MQTT pub\u002Fsub protocol solves a lot for the communication challenges for IoT applications. In fact, I would define the M as being MQTT since there are a lot of MQTT broker implementations available.",[42,37382,37383,37387],{},[63,37384,37386],{"href":29249,"rel":37385},[67],"InfluxDB"," is the popular open source time series database. Many IoT use cases are based on analyzing trends from different IoT devices. The classic examples is preventive maintenance of factory equipment. Having a time series database in your IoT architecture to record trending information will solve a lot of your data problems.",[42,37389,37390,37392],{},[63,37391,811],{"href":10415}," is the popular low-code development environment that helps create flows of data. IoT systems are often pulling data from many different sources. The data needs to be filtered, analysed or transformed before being forward to another service. Node-RED has a large community of data nodes that makes it easy to collect data from a wide variety of sources. For instance in the industrial world, Node-RED nodes are available for OPC-UA, Modbus, S7, MQTT, various PLC platforms like Opto22, etc, etc.",[42,37394,37395,37396,37401],{},"Finally ",[63,37397,37400],{"href":37398,"rel":37399},"https:\u002F\u002Fgrafana.com\u002F",[67],"Grafana"," is a popular open source visualization platform. Real time monitoring of IoT data is often the first applications deployed for IoT systems. Having graphing and dashboard technology available in your architecture makes perfect sense.",[15,37403,37404],{},"Another important feature of MING is that it can be deployed on the edge or in the cloud. IoT systems are inheritenty distributed so having the same technology available at different tiers is useful for lower barriers to adoption.",[15,37406,37407],{},"The LAMP stack was successful because it was:",[39,37409,37410,37413,37416,37419],{},[42,37411,37412],{},"Open source and freely available for anyone to adopt and use.",[42,37414,37415],{},"Highly flexible and customizable that allowed developers to adopt the stack to their use case.",[42,37417,37418],{},"Back by large developer communities creating plugins\u002Fextensions, documentation, tutorials, etc.",[42,37420,37421],{},"Easy to learn for developers with limited experience.",[15,37423,37424],{},"The MING stack has all the same characteristics. All four technologies are open source, highly flexible, back by large developer communities and relatively easy to learn. There are a lot of similarities between MING and LAMP.",[15,37426,37427],{},"Is MING relevant for IoT use cases? In my experience, people building IoT solutions are using 3-4 of the MING components. What is your experience?",[31,37429,37431],{"id":37430},"simplify-iot-complexity-with-flowfuses-mind-stack","Simplify IoT Complexity with FlowFuse’s MIND Stack",[15,37433,37434],{},"If you’re concerned about managing multiple components in your IoT stack, FlowFuse has you covered. By bundling three of the four core elements of the MING stack, MQTT, Node-RED, and Grafana. FlowFuse simplifies your setup and management. This streamlined approach means fewer moving parts and less complexity, allowing you to focus on what matters most: your data and your applications.",[15,37436,37437,37438,37442,37443,37446],{},"With the FlowFuse ",[63,37439,37441],{"href":37440},"\u002Fblog\u002F2024\u002F05\u002Fnode-red-mind-stack-with-flowfuse\u002F","MIND stack",", you get a unified platform that integrates MQTT for efficient communication, Node-RED for seamless integration and data transformation, and ",[63,37444,37445],{"href":7173},"Dashboard 2.0"," for powerful visualizations, all from one place. This integration reduces configuration and maintenance overhead while ensuring consistent performance and security across your deployments. Explore the MIND stack now.",[15,37448,37449],{},[19,37450,37451],{},"Want an easier, more secure, and scalable IoT stack? Start your [free trial]({% include \"sign-up-url.njk\" %}) of FlowFuse today.",{"title":103,"searchDepth":104,"depth":104,"links":37453},[37454],{"id":37430,"depth":107,"text":37431},"MING technology stack includes key componets in all IoT solutions M - Mosquitto\u002FMQTT, I-InfluxDB, N-Node-RED, G-Granfana","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Ftile-ming.jpg",{"excerpt":37458},{"type":12,"value":37459},[37460],[15,37461,37346,37462,37352,37465,37358],{},[63,37463,37351],{"href":37349,"rel":37464},[67],[63,37466,37357],{"href":37355,"rel":37467},[67],"\u002Fblog\u002F2023\u002F02\u002Fming-blog",{"title":37340,"description":37455},{"loc":37468},"blog\u002F2023\u002F02\u002Fming-blog","A technology stack for IoT",[8650,138,137],"r278T30xfNeSf88fFPSNlzMr5Ixu8a0BSh-0okU1EGw",{"id":37476,"title":37477,"authors":37478,"body":37479,"cta":3,"date":37558,"description":37559,"extension":116,"image":37560,"lastUpdated":3,"meta":37561,"navigation":129,"path":37566,"seo":37567,"sitemap":37568,"stem":37569,"subtitle":37570,"tags":37571,"tldr":3,"video":3,"__hash__":37572},"blog\u002Fblog\u002F2023\u002F02\u002Fhighly-available-node-red.md","Toward Highly Available Node-RED",[2622],{"type":12,"value":37480,"toc":37552},[37481,37484,37487,37496,37500,37508,37511,37517,37521,37524,37528,37537,37540,37544],[15,37482,37483],{},"Over the past few months we've held a lot of product discovery sessions and a topic\nwhich keeps coming up is \"HA Node-RED\". All software will have failures, with\nHA (high availability) the intent is to allow the workload to be processed\nregardless. There's quite a few considerations which are often not covered\nduring product discovery calls, I'm going to discuss some of those points in this article.",[15,37485,37486],{},"When my job title was software engineer I was fortunate to design and\nimplement a HA system. It's an incredibly challenging and rewarding task for\nany software engineer. As a topic, it's studied when obtaining a Computer Science\nBachelors degree, masters and even PhD. When tasked\nto make a HA system, it took me a good month to define what our goals\nwere, and what we were willing to exchange for the properties sought. This\nmight be extra hardware, engineering hours, as well as organizational challenges.\nFor now, let's focus on the first two.",[15,37488,37489,37490,37495],{},"Let's start with defining the goal; reduce the impact of a Node-RED instance\nbeing unresponsive for an arbitrary reason. In many use-cases the MTTR (Mean Time To\nRecovery) is what's measured. When for example a hardware failure takes down the instance and the time to\ndetection is zero, it will likely still take a few hours to recover. Most of\nthe recovery work is also manual, and knowledge on how to recover is usually\n",[63,37491,37494],{"href":37492,"rel":37493},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTribal_knowledge",[67],"tribal knowledge",". If the right\nperson is on-site, the right hardware is available, you kept great backups,\nand are able to deploy the new hardware right away without support from other\nfunctions you might just achieve an MTTR of 120 minutes!",[399,37497,37499],{"id":37498},"_5-10-minute-mean-time-to-recovery","5-10 minute Mean Time to Recovery",[15,37501,37502,37503,37507],{},"What's needed to bring this back to say 10 minutes? First, adopting FlowFuse will\nhelp massively here. FlowFuse can be installed on-premise, or you can use our\nmanaged Cloud offering. The software is the same, provided the on-premise\ninstall uses our ",[63,37504,37506],{"href":37505},"\u002Fdocs\u002Finstall\u002Fkubernetes\u002F","Kubernetes install"," method.",[15,37509,37510],{},"The key of the installation is the fact that the hardware layer is generalized\nas a fleet. Detecting failures is included in the install, and very fast. Comparing\nthat to most alerting systems currently, it's usually a difference between night\nand day. Furthermore, to decrease the recovery time significantly\nthere's a requirement to make software responsible for the whole procedure. Human intervention is much too slow.",[15,37512,37513,37514,29],{},"To get the MTTR down to 5 minutes there's a requirement to either make hardware\nautomatically available to the fleet, or to over-provision (more hardware is\navailable than is needed at any given moment). When a hardware failure occurs\nFlowFuse is configured to ensure all Node-RED instances that are KIA are\nreplaced. Bringing down the time to recovery to about 5 minutes.\nFor many use-cases a MTTR of 5 minutes is ",[170,37515,37516],{},"good enough",[399,37518,37520],{"id":37519},"sub-minute-mttr","Sub minute MTTR",[15,37522,37523],{},"To go below the minute, or dare I say go below 10 seconds, we'll need to increase\nthe number of running Node-RED instances. Let's start with a hot-spare. Meaning\nthere's a running Node-RED instance with the flows exactly the same as another,\nready to pick up the work when the first has some failure. Note this isn't like\na relay race, there's no baton being passed from one Node-RED to the other. While\nsome data and messages might be lost, it's possible to redirect all workload from\nthe plagued Node-RED to the hot-spare in a matter of seconds. Hot-spares taking\nover are usually only observed by humans a good few minutes after they replace a failed instance.",[399,37525,37527],{"id":37526},"sub-second","Sub second!",[15,37529,37530,37531,37536],{},"Before this post turns into a theoretical exercise we'd really need to understand\nwhich trade-offs are acceptable to you. There's the ",[63,37532,37535],{"href":37533,"rel":37534},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FCAP_theorem",[67],"CAP Theorem","\nwhich states 3 guarantees are wanted: Consistency, Availability, and Partition Tolerance. You get to pick\nonly two. In manufacturing the line must never be stopped due to a software failure where possible,\nso Availability is the most important. The question is, what comes next? Is it Consistency meaning\nall 3 instances have the same view of the global state? Or maybe Partition Tolerance where it's vital for each instance need to be able to predict the intended action even if it can't communicate to the others?",[15,37538,37539],{},"Whichever 2 you choose will dictate engineering choices in the pursuit of a great HA solution.",[399,37541,37543],{"id":37542},"the-roadmap","The roadmap",[15,37545,37546,37547,31498],{},"With FlowFuse v1.4, released February 2023, a 5 minute mean time to recovery is\nachieved for all flows running locally, that is: in the cluster. Going beyond this\nmilestone requires your input! I'd love to chat about your challenges, please\n",[63,37548,37551],{"href":37549,"rel":37550},"https:\u002F\u002Fmeetings-eu1.hubspot.com\u002Fzeger-jan",[67],"pick a timeslot to discuss your requirements",{"title":103,"searchDepth":104,"depth":104,"links":37553},[37554,37555,37556,37557],{"id":37498,"depth":294,"text":37499},{"id":37519,"depth":294,"text":37520},{"id":37526,"depth":294,"text":37527},{"id":37542,"depth":294,"text":37543},"2023-02-15","Often requested features for Node-RED include HA or Highly Available","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Froadmap-unsplash.jpg",{"excerpt":37562},{"type":12,"value":37563},[37564],[15,37565,37483],{},"\u002Fblog\u002F2023\u002F02\u002Fhighly-available-node-red",{"title":37477,"description":37559},{"loc":37566},"blog\u002F2023\u002F02\u002Fhighly-available-node-red","How mission critical use-cases can be supported through FlowFuse soon",[8650,138,10],"YmYsgkGhHcl8EbBZmNPehHXrwCSnX2qiDZptGlVL_Ic",{"id":37574,"title":29175,"authors":37575,"body":37576,"cta":3,"date":37638,"description":37639,"extension":116,"image":29183,"lastUpdated":6064,"meta":37640,"navigation":129,"path":37645,"seo":37646,"sitemap":37647,"stem":37648,"subtitle":30352,"tags":37649,"tldr":3,"video":3,"__hash__":37650},"blog\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-1.md",[19494],{"type":12,"value":37577,"toc":37633},[37578,37581,37585,37588,37594,37598,37606,37609,37615,37619,37622,37629],[15,37579,37580],{},"There is usually more than one way to complete a given task in software and Node-RED is no exception. In this blog post we are going to share three useful tips to save yourself time when working on your flows.",[399,37582,37584],{"id":37583},"_1-use-controlleft-click-to-search-your-nodes","1. Use control+left-click to search your nodes",[15,37586,37587],{},"Sometimes it's quicker to search for a node using its name rather than scrolling through the palette. Simply hold control then left-click to bring up a searchable list.",[15,37589,37590],{},[165,37591],{"alt":37592,"src":37593,"title":37592},"Selecting a node without having to use the palette","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fload-node.gif",[399,37595,37597],{"id":37596},"_2-split-sections-of-your-code-using-the-link-nodes","2. Split sections of your code using the link nodes",[15,37599,37600,37601,37605],{},"If you want to separate your flow into two distinct sections, link nodes are a great way to format your work. As we covered in our blog on ",[63,37602,37604],{"href":37603},"\u002Fblog\u002F2022\u002F12\u002Fnode-red-flow-best-practice","Node-RED best practices",", the combination of link nodes and grouped flows is very powerful.",[15,37607,37608],{},"To split your flow select the input and output nodes then right click, select 'Show Action List' and then type 'split'. Select 'Split wire with link nodes'.",[15,37610,37611],{},[165,37612],{"alt":37613,"src":37614,"title":37613},"Spliting your nodes with link nodes","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fsplit-with-link.gif",[399,37616,37618],{"id":37617},"_3-link-multiple-inputs-and-outputs-in-one-command","3. Link multiple inputs and outputs in one command",[15,37620,37621],{},"Once a switch node has several outputs it can be slow to manually wire each to the new node. Using the action menu (right click), select 'Show Action List' then 'Wire Node to Multiple' this option will join everything up in one step.",[15,37623,37624],{},[165,37625],{"alt":37626,"src":37627,"title":37628},"\"Linking multiple inputs and outputs in one command\"","\u002Fblog\u002F2023\u002F02\u002Fimages\u002Fjoin-wires.gif","Linking multiple inputs and outputs in one command",[15,37630,29135,37631,29],{},[63,37632,12885],{"href":29138},{"title":103,"searchDepth":104,"depth":104,"links":37634},[37635,37636,37637],{"id":37583,"depth":294,"text":37584},{"id":37596,"depth":294,"text":37597},{"id":37617,"depth":294,"text":37618},"2023-02-07","Learn three valuable Node-RED tips to enhance your workflow: search nodes efficiently, split code sections with link nodes, and link multiple inputs\u002Foutputs in one command.",{"excerpt":37641},{"type":12,"value":37642},[37643],[15,37644,37580],{},"\u002Fblog\u002F2023\u002F02\u002F3-quick-node-red-tips-1",{"title":29175,"description":37639},{"loc":37645},"blog\u002F2023\u002F02\u002F3-quick-node-red-tips-1",[8650,138,7457,11298],"XLF-zCCv0yviIZ4gnJNUV4BvHgcZk5XZ7Pc2rJMUzGw",{"id":37652,"title":37653,"authors":37654,"body":37655,"cta":37821,"date":37824,"description":37825,"extension":116,"image":3,"lastUpdated":6203,"meta":37826,"navigation":129,"path":37832,"seo":37833,"sitemap":37834,"stem":37835,"subtitle":37836,"tags":37837,"tldr":37838,"video":3,"__hash__":37839},"blog\u002Fblog\u002F2023\u002F01\u002Fenvironment-variables-in-node-red.md","Using Environment Variables in Node-RED (2026)",[2622],{"type":12,"value":37656,"toc":37814},[37657,37660,37670,37684,37690,37708,37716,37720,37731,37745,37749,37752,37759,37763,37766,37773,37777],[15,37658,37659],{},"Programs, written with Node-RED or otherwise, need to sometimes retrieve information that wasn’t decided on during the creation of the program.",[15,37661,37662,37663,37665,37666,37669],{},"Contextual data like configuration, which user is executing the code, differentiate based on what device is executing a flow, or sometimes secrets which shouldn’t be exposed in the code. This is usually done through environment variables. These are pairs of strings, a key with an attached value, which are accessed by their key. Say you want to access an API endpoint with a key, you’d save the key as ",[924,37664,16264],{}," with the value set to ",[924,37667,37668],{},"yoursupersecretkey",". FlowFuse allows setting environment variables. Let’s start using them to understand how they work.",[15,37671,37672,37673,37675,37676,37679,37680,37683],{},"One of the options for the ",[924,37674,1588],{}," node is to inject a ",[924,37677,37678],{},"env variable",", short for; you guessed it: Environment Variable. In this case we’re going to one that’s pre-defined by Node-RED: ",[924,37681,37682],{},"NR_FLOW_NAME",". The name of each variable is in all caps by convention. When connecting this inject to a debug it prints “Flow 1” for me.",[15,37685,37686],{},[165,37687],{"alt":37688,"src":37689,"title":37688},"Using an environment variable in Node-RED","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fnode-red-use-env-var.png",[15,37691,37692,37693,853,37695,37697,37698,37700,37701,37704,37705,37707],{},"Leveraging environment variables can also be done with other nodes, like for example ",[924,37694,9518],{},[924,37696,9482],{},". Note however; you can set the ",[924,37699,1588],{}," node to output the value for ",[924,37702,37703],{},"FOO"," even when it doesn’t exist, but it doesn’t allow you to check in the switch node for example if ",[924,37706,37703],{}," exists.",[15,37709,37710,37711,37715],{},"Node-RED allows you to set environment variables, but not to change them when executing flows. If you want to update data during execution, look into using ",[63,37712,37714],{"href":37713},"\u002Fdocs\u002Fuser\u002Fpersistent-context\u002F","persistent context",". Node-RED doesn’t support Environment Variables like other programming environments do. When the flow is deployed the environment variables are replaced with the known values at that time. This is the biggest gotcha for most developers.",[399,37717,37719],{"id":37718},"predefined-variables","Predefined variables",[15,37721,37722,37723,853,37726,4972,37729,29],{},"Our first example was using a predefined variable, exposed by Node-RED. As of 3.0 it exposes a few environment variables among which ",[924,37724,37725],{},"NR_NODE_NAME",[924,37727,37728],{},"NR_GROUP_NAME",[924,37730,37682],{},[15,37732,37733,37735,37736,37739,37740,37744],{},[63,37734,803],{"href":810}," extends this list with for example a ",[924,37737,37738],{},"FF_PROJECT_ID"," allowing you to for example understand what group of instances sent a certain message, but also sets them for each ",[63,37741,37743],{"href":37742},"\u002Fdocs\u002Fdevice-agent\u002Fintroduction\u002F","device agent",". This allows users to pinpoint which device sent a message, for example to update a dashboard accordingly.",[399,37746,37748],{"id":37747},"managing-environments-variables","Managing environments variables",[15,37750,37751],{},"In FlowFuse it’s easy to manage variables set for instances. Under settings in the environment tab it’s a form to set them. You’ll have to restart your instances to make them available in the cloud, and update the target snapshot for devices. When done, these are available.",[15,37753,37754],{},[165,37755],{"alt":37756,"src":37757,"title":37758},"\"Setting a environment variable in FlowFuse\"","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fflowforge-set-env-var.png","Setting a environment variable in FlowFuse",[31,37760,37762],{"id":37761},"boost-your-node-red-security-with-flowfuse","Boost Your Node-RED Security with FlowFuse",[15,37764,37765],{},"FlowFuse provides a comprehensive platform for managing and securing your Node-RED solutions. It includes advanced security features such as role-based access control, Multi-factor Authentication (MFA), Single Sign-On (SSO), and encryption to protect your data and enhance operational efficiency.",[15,37767,37768,37769,29],{},"Learn how FlowFuse can boost your Node-RED security and streamline management through the ",[63,37770,37772],{"href":37771},"\u002Fplatform\u002Fsecurity\u002F#application","FlowFuse security statement",[399,37774,37776],{"id":37775},"explore-more-on-security","Explore More on Security",[39,37778,37779,37785,37791,37797,37802,37808],{},[42,37780,37781],{},[63,37782,37784],{"href":37783},"\u002Fblog\u002F2024\u002F04\u002Frole-based-access-control-rbac-for-node-red-with-flowfuse\u002F","Role-Based Access Control (RBAC) for Node-RED with FlowFuse",[42,37786,37787],{},[63,37788,37790],{"href":37789},"\u002Fdocs\u002Fuser\u002Fdevops-pipelines\u002F#protected-instances","Protecting Instances from Being Modified",[42,37792,37793],{},[63,37794,37796],{"href":37795},"\u002Fblog\u002F2024\u002F07\u002Fhow-to-setup-sso-ldap-for-the-node-red\u002F","How to Set Up SSO LDAP for Node-RED",[42,37798,37799],{},[63,37800,12958],{"href":37801},"\u002Fblog\u002F2024\u002F07\u002Fhow-to-setup-sso-saml-for-the-node-red\u002F",[42,37803,37804],{},[63,37805,37807],{"href":37806},"\u002Fblog\u002F2024\u002F03\u002Fhttp-authentication-node-red-with-flowfuse\u002F","Securing HTTP Traffic for Node-RED with FlowFuse",[42,37809,37810],{},[63,37811,37813],{"href":37812},"\u002Fblog\u002F2024\u002F01\u002Fsoc2\u002F","FlowFuse is now SOC 2 Type 1 Compliant",{"title":103,"searchDepth":104,"depth":104,"links":37815},[37816,37817,37818],{"id":37718,"depth":294,"text":37719},{"id":37747,"depth":294,"text":37748},{"id":37761,"depth":107,"text":37762,"children":37819},[37820],{"id":37775,"depth":294,"text":37776},{"type":748,"title":37822,"description":37823},"Manage Node-RED Environment Variables with FlowFuse","FlowFuse makes it easy to set, update, and secure environment variables across all your Node-RED instances and devices, with role-based access control and centralized management from a single dashboard.","2023-01-27","Node-RED supports environment variables (env vars) slight different, how to use it and the gotcha's are explained in this article.",{"keywords":37827,"excerpt":37828},"node-red environment variables, env vars node-red, node-red env variable, flowfuse environment variables, node-red configuration",{"type":12,"value":37829},[37830],[15,37831,37659],{},"\u002Fblog\u002F2023\u002F01\u002Fenvironment-variables-in-node-red",{"title":37653,"description":37825},{"loc":37832},"blog\u002F2023\u002F01\u002Fenvironment-variables-in-node-red","Predefined data to be used in your Node-RED instance",[8650,138,7457],"Node-RED environment variables are read-only key-value pairs resolved at deploy time, useful for storing configuration and secrets without hardcoding them in flows. Node-RED exposes predefined variables like NR_FLOW_NAME, and FlowFuse extends this with instance and device identifiers. You can set and manage variables for all your instances directly from the FlowFuse Environment settings tab.","bM2-H7R1Nkz_vefyaDrHkJLmTkQBgVJNFLY5lp0MhU0",{"id":37841,"title":37842,"authors":37843,"body":37844,"cta":3,"date":38087,"description":38088,"extension":116,"image":38089,"lastUpdated":38090,"meta":38091,"navigation":129,"path":38096,"seo":38097,"sitemap":38098,"stem":38099,"subtitle":19737,"tags":38100,"tldr":3,"video":3,"__hash__":38101},"blog\u002Fblog\u002F2023\u002F01\u002Fgetting-started-with-node-red.md","Getting Started with Node-RED",[19494],{"type":12,"value":37845,"toc":38075},[37846,37849,37852,37856,37859,37861,37864,37867,37870,37873,37891,37894,37903,37917,37921,37934,37953,37960,37973,37982,37989,37997,38004,38012,38016,38029,38033,38036,38040,38043,38047,38050,38058,38060,38073],[15,37847,37848],{},"Node-RED is a visual programming tool for working with IoT devices and web services. It allows users to create flows using a drag-and-drop interface, making it easy to connect different nodes together to build powerful automations.",[15,37850,37851],{},"In this blog post, we'll take a look at how to get started with Node-RED and create some basic flows. We'll also explore the palette manager, a powerful feature that allows users to install and manage additional nodes for Node-RED.",[399,37853,37855],{"id":37854},"installing-node-red","Installing Node-RED",[15,37857,37858],{},"First, you'll need to get an installation of Node-RED up and running. There are several ways to do this. We suggest using FlowFuse as it's very easy to get Node-RED running. You can also install Node-RED locally using npm (Node Package Manager), which comes with Node.js.",[1165,37860,803],{"id":10},[15,37862,37863],{},"To get Node-RED running on FlowFuse [sign up as a new user]({% include \"sign-up-url.njk\" %}). New users are enrolled in a trial and a Node-RED instance will be started for you within a minute.",[15,37865,37866],{},"Once that instance has booted up you can access Node-RED by pressing \"Open Editor\".",[1165,37868,37869],{"id":37869},"npm",[15,37871,37872],{},"To install Node-RED locally using npm, open up your terminal and type the following command:",[1756,37874,37876],{"className":13415,"code":37875,"language":13417,"meta":103,"style":103},"npm install -g node-red\n",[924,37877,37878],{"__ignoreMap":103},[1107,37879,37880,37882,37885,37888],{"class":1765,"line":1766},[1107,37881,37869],{"class":10543},[1107,37883,37884],{"class":1966}," install",[1107,37886,37887],{"class":1966}," -g",[1107,37889,37890],{"class":1966}," node-red\n",[15,37892,37893],{},"Once Node-RED is installed, you can start it by running the following command:",[1756,37895,37897],{"className":13415,"code":37896,"language":13417,"meta":103,"style":103},"node-red\n",[924,37898,37899],{"__ignoreMap":103},[1107,37900,37901],{"class":1765,"line":1766},[1107,37902,37896],{"class":10543},[15,37904,37905,37906,37911,37912,29],{},"This will start the Node-RED server and open up the ",[63,37907,37910],{"href":37908,"rel":37909},"http:\u002F\u002Flocalhost:1880",[67],"editor in your web browser",". You can also specify a different port or a settings file if you want to. If you want to run Node-RED locally but manage it remotely through FlowFuse, check out ",[63,37913,37916],{"href":37914,"rel":37915},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2025\u002F09\u002Finstalling-node-red\u002F",[67],"this guide",[399,37918,37920],{"id":37919},"first-flow","First Flow",[15,37922,37923,37924,853,37927,37930,37931,37933],{},"Now that you have Node-RED running, let's take a look at how to create a simple flow. In this example, we'll create a simple \"Hello World\" endpoint. To do this, we'll use the ",[924,37925,37926],{},"http in",[924,37928,37929],{},"http response",", and the ",[924,37932,9518],{}," nodes, which can be found in the common nodes menu on the left of Node-RED.",[15,37935,37936,37937,37939,37940,37942,37943,1720,37945,37947,37948,1720,37950,37952],{},"First, drag an ",[924,37938,37926],{}," node into the editor. This node will listen for incoming HTTP requests. Next drag in the \"change\" and the ",[924,37941,37929],{}," node into the editor. Connect the ",[924,37944,37926],{},[924,37946,9518],{}," node and connect the ",[924,37949,9518],{},[924,37951,37929],{}," node. Your flow should look similar to this:",[15,37954,37955],{},[165,37956],{"alt":37957,"src":37958,"title":37959},"\"Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration.\"","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fthree-nodes.png","Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration.",[15,37961,37962,37963,37965,37966,37969,37970,29],{},"To configure the ",[924,37964,37926],{}," node, double-click on it to open its properties. Here, you can set the URL that the node will listen to, as well as the method (GET, POST, etc.). In this example, we'll set the URL to ",[924,37967,37968],{},"\u002Fhello"," and the method to ",[924,37971,37972],{},"GET",[15,37974,37975,37976,37978,37979,37981],{},"Now we need to set what the endpoint will respond with, we will do that in the ",[924,37977,9518],{}," node. Double-click the ",[924,37980,9518],{}," node then add \"Hello World\" to the field which says \"to the value\". It should look like this:",[15,37983,37984],{},[165,37985],{"alt":37986,"src":37987,"title":37988},"\"Configuring the change node to set the payload to Hello World\"","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fset-reply.png","Configuring the change node to set the payload to Hello World",[15,37990,37962,37991,37993,37994,37996],{},[924,37992,37929],{}," node, double-click on it to open its properties. Here, you should set the \"Status Code\" to be 200. This is not vital for the demo to work but it's good practice to return the correct codes when something connects to an API. Status code 200 means the API responded OK. This is how your ",[924,37995,37929],{}," node should look:",[15,37998,37999],{},[165,38000],{"alt":38001,"src":38002,"title":38003},"\"Configuring the status node to set the response to 200\"","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fresponse-code.png","Configuring the status node to set the response to 200",[15,38005,38006,38007,29],{},"You can read more about HTTP response codes in ",[63,38008,38011],{"href":38009,"rel":38010},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FList_of_HTTP_status_codes",[67],"this article",[399,38013,38015],{"id":38014},"testing-your-flow","Testing Your Flow",[15,38017,38018,38019,38023,38024,38028],{},"Now that we have our flow set up, we can deploy it by clicking the \"Deploy\" button in the top right corner of the editor. Once the flow is deployed, you can test it by opening up a web browser. If you installed Node-RED using npm navigate to \"",[63,38020,38021],{"href":38021,"rel":38022},"http:\u002F\u002Flocalhost:1880\u002Fhello",[67],"\". If you are working on FlowFuse and running cloud hosted instance, use your instance URL with \"\u002Fhello\" added to the end, it should look something like \"",[63,38025,38026],{"href":38026,"rel":38027},"https:\u002F\u002Fyour-instance-name.flowfuse.cloud\u002Fhello",[67],"\". You should see \"Hello World!\" displayed in the browser.",[399,38030,38032],{"id":38031},"debug-output","Debug Output",[15,38034,38035],{},"One of the most powerful features in Node-RED is the ability to debug your flow. This can be done by adding a debug node to your flow and connecting it to the nodes you want to debug. When a message is sent through the connected node, the debug node will print the message in the debug sidebar on the right side of the editor. This can be very helpful when trying to understand how a flow is working or troubleshoot any issues.",[399,38037,38039],{"id":38038},"the-palette-manager","The Palette Manager",[15,38041,38042],{},"In addition to the built-in nodes, Node-RED also has a palette manager feature which allows users to easily install and manage additional nodes from the community. To access the palette manager, go to the menu in the top right corner and select \"Manage Palette\". Here, you can search for and install new nodes, as well as update or remove existing ones. This is a great way to extend the functionality of Node-RED and add new capabilities to your flows.",[399,38044,38046],{"id":38045},"import-the-flow","Import the flow",[15,38048,38049],{},"If you want to view this flow you can import it using the code below. Copy the code then select Import from the top right menu in Node-RED. Paste the code into the field then press Import.",[15,38051,4917,38052,1150],{},[1107,38053,38054,38055,38057],{},"\n{\n\"id\": \"a742e7a95697bb40\",\n\"type\": \"http in\",\n\"z\": \"9e9af3caa4dc14d3\",\n\"name\": \"\",\n\"url\": \"\u002Fhello\",\n\"method\": \"get\",\n\"upload\": false,\n\"swaggerDoc\": \"\",\n\"x\": 180,\n\"y\": 200,\n\"wires\": [\n[\n\"883e7d597f7c7c4b\"\n]\n]\n},\n{\n\"id\": \"aca024dcb79bdb92\",\n\"type\": \"http response\",\n\"z\": \"9e9af3caa4dc14d3\",\n\"name\": \"\",\n\"statusCode\": \"200\",\n\"headers\": {},\n\"x\": 500,\n\"y\": 200,\n\"wires\": ",[1107,38056],{},"\n},\n{\n\"id\": \"883e7d597f7c7c4b\",\n\"type\": \"change\",\n\"z\": \"9e9af3caa4dc14d3\",\n\"name\": \"\",\n\"rules\": [\n{\n\"t\": \"set\",\n\"p\": \"payload\",\n\"pt\": \"msg\",\n\"to\": \"Hello World\",\n\"tot\": \"str\"\n}\n],\n\"action\": \"\",\n\"property\": \"\",\n\"from\": \"\",\n\"to\": \"\",\n\"reg\": false,\n\"x\": 340,\n\"y\": 200,\n\"wires\": [\n[\n\"aca024dcb79bdb92\"\n]\n]\n}\n",[399,38059,2568],{"id":2567},[15,38061,38062,38063,38067,38068,29],{},"Well done, you've now got your first flow up and running. Enjoy using Node-RED and thanks for reading. Now if you want to start with your first beginner friendly project, building a weather dashboard is great, read this ",[63,38064,38066],{"href":38065},"\u002Fblog\u002F2025\u002F12\u002Fgetting-weather-data-in-node-red\u002F","article for getting started",". If you'd like to dive deeper into more Node-RED capabilities and how it can help in an enterprise setting, check out our ",[63,38069,38072],{"href":38070,"rel":38071},"https:\u002F\u002Fflowfuse.com\u002Febooks\u002Fbeginner-guide-to-a-professional-nodered\u002F",[67],"eBook The Ultimate Beginner Guide to a Professional Node-RED",[2582,38074,13741],{},{"title":103,"searchDepth":104,"depth":104,"links":38076},[38077,38081,38082,38083,38084,38085,38086],{"id":37854,"depth":294,"text":37855,"children":38078},[38079,38080],{"id":10,"depth":104,"text":803},{"id":37869,"depth":104,"text":37869},{"id":37919,"depth":294,"text":37920},{"id":38014,"depth":294,"text":38015},{"id":38031,"depth":294,"text":38032},{"id":38038,"depth":294,"text":38039},{"id":38045,"depth":294,"text":38046},{"id":2567,"depth":294,"text":2568},"2023-01-23","In this article we are going to explain the first things you need to know to get started with Node-RED.","\u002Fblog\u002F2023\u002F01\u002Fimages\u002Fgetting-started-nr.png","2025-12-16",{"excerpt":38092},{"type":12,"value":38093},[38094],[15,38095,37848],{},"\u002Fblog\u002F2023\u002F01\u002Fgetting-started-with-node-red",{"title":37842,"description":38088},{"loc":38096},"blog\u002F2023\u002F01\u002Fgetting-started-with-node-red",[8650,138,7457],"FXta9iv2Aw_bg1VgvncZrBUPJ_mHWDSymDvjLwvcCGk",{"id":38103,"title":38104,"authors":38105,"body":38106,"cta":3,"date":38132,"description":38133,"extension":116,"image":3,"lastUpdated":3,"meta":38134,"navigation":129,"path":38139,"seo":38140,"sitemap":38141,"stem":38142,"subtitle":38143,"tags":38144,"tldr":3,"video":3,"__hash__":38145},"blog\u002Fblog\u002F2022\u002F12\u002Fwhat-flowforge-adds-to-node-red.md","Why you need FlowFuse when you already have Node-RED?",[19494],{"type":12,"value":38107,"toc":38130},[38108,38111,38114,38117,38120,38123],[15,38109,38110],{},"Many organizations face challenges in IT\u002FOT convergence. There are many protocols, disparate devices, and everything is a brownfield project. Furthermore, skilled engineers are hard to find. Many organizations find themselves maintaining systems and integrations, and have little time left to improve continuously.",[15,38112,38113],{},"This is why Node-RED is adopted so widely, it makes a world of difference through two of its properties: Low-Code Development and the ability to integrate with a wide range of hardware and software systems. Node-RED makes it easy to program data collection, then use that data for decision making, and provide feedback to both your digital platform and the physical reality of your business. Furthermore, Node-RED is unrivalled in making data accessible. The data previously locked in a walled garden of your hardware providers is now accessible and obtainable. With that, more data is available and better decisions are made, faster.",[15,38115,38116],{},"FlowFuse makes developer collaboration, flow deployment, and scaling of infrastructure easy when working with Node-RED. It offers an intuitive user interface for creating, deploying, monitoring, and managing multiple Node-RED projects. FlowFuse also provides one-click deployment to thousands of devices, making it easy to manage large-scale environments.",[15,38118,38119],{},"These features make FlowFuse a valuable tool for organizations using Node-RED to build applications and automate processes. Enabling for further adoption of Node-RED to integrate more systems, for even better decisions.",[15,38121,38122],{},"This not only reduces operational costs but it also allows an organization to be far more agile with their Node-RED projects than was possible without FlowFuse.",[15,38124,38125,38126,38129],{},"We would be happy to talk to you more about how FlowFuse can help you get the most value from Node-RED. Please ",[63,38127,38128],{"href":2989},"contact us"," to learn more.",{"title":103,"searchDepth":104,"depth":104,"links":38131},[],"2022-12-15","Discover how FlowFuse enhances Node-RED with easy developer collaboration, flow deployment, and infrastructure scaling. Maximize the value of your Node-RED projects effortlessly.",{"excerpt":38135},{"type":12,"value":38136},[38137],[15,38138,38110],{},"\u002Fblog\u002F2022\u002F12\u002Fwhat-flowforge-adds-to-node-red",{"title":38104,"description":38133},{"loc":38139},"blog\u002F2022\u002F12\u002Fwhat-flowforge-adds-to-node-red","FlowFuse makes developer collaboration, flow deployment, and scaling of infrastructure easy when working with Node-RED",[8650,10,138],"pEAsMuQVAtklmhMtiEb1cFAdeT8hU5bjv65zOLHv6Io",{"id":38147,"title":38148,"authors":38149,"body":38150,"cta":3,"date":38238,"description":38239,"extension":116,"image":3,"lastUpdated":3,"meta":38240,"navigation":129,"path":38245,"seo":38246,"sitemap":38247,"stem":38248,"subtitle":38249,"tags":38250,"tldr":3,"video":3,"__hash__":38251},"blog\u002Fblog\u002F2022\u002F12\u002Fcreate-http-trigger-with-authentication.md","Create HTTP triggers with authentication",[19494],{"type":12,"value":38151,"toc":38234},[38152,38155,38158,38162,38169,38172,38178,38181,38187,38190,38196,38199,38203,38206,38209,38215,38225,38231],[15,38153,38154],{},"Having an HTTP endpoint trigger your flows is very useful. From any browser or command line you now have the ability to trigger your flows.",[15,38156,38157],{},"Doing so safely with authentication is slightly harder, but not a lot. FlowFuse makes it rather easy to accomplish.",[399,38159,38161],{"id":38160},"creating-the-http-flow","Creating the HTTP flow",[15,38163,38164,38165,38168],{},"When you start a project on FlowFuse, remember the project name. For this how-to we’ll use ",[924,38166,38167],{},"example",". Open the editor and drag in the HTTP In node as well as the HTTP response node. Connect them, and add a debug node, which is connected to the “HTTP in” node.",[15,38170,38171],{},"First off; let’s set the HTTP in node properties:",[15,38173,38174],{},[165,38175],{"alt":38176,"src":38177,"title":38176},"Shows the UI to edit the node's properties","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fedit-http-node.png",[15,38179,38180],{},"You can import this flow into your own project if you’d like:",[1756,38182,38185],{"className":38183,"code":38184,"language":15053},[15051],"[{\"id\":\"4faa84d37a52bb28\",\"type\":\"group\",\"z\":\"3c6e2dc732ada815\",\"name\":\"Allow HTTP Post request to trigger a flow\",\"style\":{\"label\":!0},\"nodes\":[\"1fa26e0ed3ddec1a\",\"45a180052e1a2f43\",\"09347881f4fa4057\"],\"x\":34,\"y\":79,\"w\":472,\"h\":122},{\"id\":\"1fa26e0ed3ddec1a\",\"type\":\"http in\",\"z\":\"3c6e2dc732ada815\",\"g\":\"4faa84d37a52bb28\",\"name\":\"HTTP Trigger\",\"url\":\"\u002Fhttp-trigger\",\"method\":\"post\",\"upload\":!1,\"swaggerDoc\":\"\",\"x\":130,\"y\":120,\"wires\":[[\"45a180052e1a2f43\",\"09347881f4fa4057\"]]},{\"id\":\"45a180052e1a2f43\",\"type\":\"http response\",\"z\":\"3c6e2dc732ada815\",\"g\":\"4faa84d37a52bb28\",\"name\":\"Empty HTTP response\",\"statusCode\":\"200\",\"headers\":{},\"x\":360,\"y\":120,\"wires\":[]},{\"id\":\"09347881f4fa4057\",\"type\":\"debug\",\"z\":\"3c6e2dc732ada815\",\"g\":\"4faa84d37a52bb28\",\"name\":\"Print HTTP Request\",\"active\":!0,\"tosidebar\":!0,\"console\":!1,\"tostatus\":!1,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":360,\"y\":160,\"wires\":[]}]\n",[924,38186,38184],{"__ignoreMap":103},[15,38188,38189],{},"Now I’ve opened a terminal and executed:",[1756,38191,38194],{"className":38192,"code":38193,"language":15053},[15051],"curl -X POST https:\u002F\u002Fexample.flowforge.com\u002Fhttp-trigger\n",[924,38195,38193],{"__ignoreMap":103},[15,38197,38198],{},"When there’s no output, that means it’s all good! There should be an empty message in the debug console in the Node-RED editor though",[399,38200,38202],{"id":38201},"securing-the-http-trigger-with-a-username-and-password","Securing the HTTP trigger with a username and password",[15,38204,38205],{},"The problem with our trigger is that anyone with internet access could trigger it. That’s not a great idea. So let’s secure this endpoint with HTTP Basic Authentication. There are various ways to include a secure endpoint in Node-RED, we’ve built authentication directly into FlowFuse to make it easier for all users. On the FlowFuse project, go to settings and then to ‘Editor’. Under the section HTTP Auth you can set a username and password. You should generate both by a random string generator, and store the credentials somewhere safe.  Restart the project to have the runtime pick up the changes, and the endpoint is secured!",[15,38207,38208],{},"Let’s validate the endpoint that worked a minute ago doesn’t anymore:",[1756,38210,38213],{"className":38211,"code":38212,"language":15053},[15051],"curl -X POST https:\u002F\u002Fexample.flowforge.cloud\u002Fhttp-trigger\n=> Unauthorized\n",[924,38214,38212],{"__ignoreMap":103},[15,38216,38217,38218],{},"Let’s get it working again: (replace ",[38219,38220,1413,38221],"username",{},[38222,38223,38224],"password",{}," with the details from the sticky note)",[1756,38226,38229],{"className":38227,"code":38228,"language":15053},[15051],"curl -X POST https:\u002F\u002F\u003Cusername>:\u003Cpassword>@example.flowforge.cloud\u002Fhttp-trigger\n",[924,38230,38228],{"__ignoreMap":103},[15,38232,38233],{},"That’s it! You now have a flow that’s protected by a username and password combination!",{"title":103,"searchDepth":104,"depth":104,"links":38235},[38236,38237],{"id":38160,"depth":294,"text":38161},{"id":38201,"depth":294,"text":38202},"2022-12-07","Learn how to create HTTP triggers with authentication in Node-RED using FlowFuse. Securely trigger flows from any browser or command line while safeguarding your endpoints with HTTP Basic Authentication.",{"excerpt":38241},{"type":12,"value":38242},[38243],[15,38244,38154],{},"\u002Fblog\u002F2022\u002F12\u002Fcreate-http-trigger-with-authentication",{"title":38148,"description":38239},{"loc":38245},"blog\u002F2022\u002F12\u002Fcreate-http-trigger-with-authentication","From any browser or command line you now have the ability to securely trigger your flows",[8650,138,7457],"LvBjij9rgFLTrm1-7c41ZgogAarP8Uy5Wajn2rsPh9Q",{"id":38253,"title":38254,"authors":38255,"body":38256,"cta":3,"date":38553,"description":38554,"extension":116,"image":3,"lastUpdated":3,"meta":38555,"navigation":129,"path":37603,"seo":38560,"sitemap":38561,"stem":38562,"subtitle":38563,"tags":38564,"tldr":3,"video":3,"__hash__":38565},"blog\u002Fblog\u002F2022\u002F12\u002Fnode-red-flow-best-practice.md","Format your Node-RED flows for better team collaboration",[19494],{"type":12,"value":38257,"toc":38520},[38258,38261,38264,38268,38277,38281,38288,38292,38299,38303,38306,38310,38317,38321,38328,38332,38335,38339,38346,38350,38357,38361,38364,38368,38375,38379,38386,38390,38393,38397,38404,38408,38415,38424,38427,38431,38438,38442,38449,38453,38456,38460,38467,38471,38478,38482,38491,38510,38515,38517],[15,38259,38260],{},"When it comes to working on Node-RED flows as part of a team, there are a few best practices that can make things go more smoothly.",[15,38262,38263],{},"From formatting your flows for readability to providing clear comments on nodes and groups, a little bit of effort upfront can save your team a lot of headaches down the road. In this post, we'll cover some of the main things to keep in mind when working on Node-RED flows as part of a team.",[399,38265,38267],{"id":38266},"give-your-groups-descriptive-names","Give your groups descriptive names",[15,38269,38270,38271,38276],{},"Let’s start with ",[63,38272,38275],{"href":38273,"rel":38274},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Feditor\u002Fworkspace\u002Fgroups",[67],"grouping your flows"," and giving each group a clear explanation of what it does. Compare the first to the second example below and consider how much more quickly you can understand what the flow is doing.",[1165,38278,38280],{"id":38279},"this-is-not-helpful-time-doesnt-tell-you-enough-to-understand-the-flows-purpose","This is not helpful, 'Time' doesn't tell you enough to understand the flow's purpose.",[15,38282,38283],{},[165,38284],{"alt":38285,"src":38286,"title":38287},"\"Screenshot showing the example of flow having the bad group name\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fname-bad.png","Screenshot showing the example of flow having the bad group name",[1165,38289,38291],{"id":38290},"this-is-much-better-we-know-what-the-flow-is-doing-without-inspecting-the-nodes","This is much better, we know what the flow is doing without inspecting the nodes.",[15,38293,38294],{},[165,38295],{"alt":38296,"src":38297,"title":38298},"\"Screenshot showing the example of flow having the good group name\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fname-good.png","Screenshot showing the example of flow having the good group name",[399,38300,38302],{"id":38301},"explain-what-your-switches-do","Explain what your switches do",[15,38304,38305],{},"Try to make it obvious what each switch does without having to open the node editor. Ask a question in the switch's name and make a positive answer the top connection out.",[1165,38307,38309],{"id":38308},"this-is-not-easy-to-understand-what-does-the-switch-do","This is not easy to understand, what does the switch do?",[15,38311,38312],{},[165,38313],{"alt":38314,"src":38315,"title":38316},"\"Screenshot showing the example of flow having the switch with bad name\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fswitch-bad.png","Screenshot showing the example of flow having the switch with bad name",[1165,38318,38320],{"id":38319},"this-is-a-lot-better-we-can-see-that-the-top-debug-should-be-triggered","This is a lot better, we can see that the top debug should be triggered.",[15,38322,38323],{},[165,38324],{"alt":38325,"src":38326,"title":38327},"\"Screenshot showing the example of flow having the switch with good name\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fswitch-good.png","Screenshot showing the example of flow having the switch with good name",[399,38329,38331],{"id":38330},"where-possible-your-flows-should-work-down-the-canvas","Where possible your flows should work down the canvas",[15,38333,38334],{},"It makes it so much easier to understand what happens and in which order if your flows start at the top of the canvas and work down to the bottom.",[1165,38336,38338],{"id":38337},"this-is-almost-unreadable-its-very-hard-to-work-out-the-order-of-the-groups","This is almost unreadable, it's very hard to work out the order of the groups.",[15,38340,38341],{},[165,38342],{"alt":38343,"src":38344,"title":38345},"\"Screenshot showing an example of flow that doesn't work down the canvas\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fflowdown-bad.png","Screenshot showing an example of flow that doesn't work down the canvas",[1165,38347,38349],{"id":38348},"where-as-this-is-so-much-easier-to-understand","Where as this is so much easier to understand.",[15,38351,38352],{},[165,38353],{"alt":38354,"src":38355,"title":38356},"\"Screenshot showing an example of flow that works down the canvas\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fflowdown-good.png","Screenshot showing an example of flow that works down the canvas",[399,38358,38360],{"id":38359},"use-link-nodes-rather-than-wires-to-join-groups","Use link nodes rather than wires to join groups",[15,38362,38363],{},"Groups should not be joined using wires, it just looks untidy and quickly reduces readability of your flows.",[1165,38365,38367],{"id":38366},"the-wire-is-blocking-the-title-it-only-gets-worse-as-you-add-more-wires","The wire is blocking the title, it only gets worse as you add more wires.",[15,38369,38370],{},[165,38371],{"alt":38372,"src":38373,"title":38374},"\"Screenshot showing an example of flow with wires blocking group titles\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Flink-bad.png","Screenshot showing an example of flow with wires blocking group titles",[1165,38376,38378],{"id":38377},"you-can-see-the-group-titles-easily-now","You can see the group titles easily now.",[15,38380,38381],{},[165,38382],{"alt":38383,"src":38384,"title":38385},"\"Screenshot showing an example of flow with link nodes improving readability\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Flink-good.png","Screenshot showing an example of flow with link nodes improving readability",[399,38387,38389],{"id":38388},"keep-your-groups-compact","Keep your groups compact",[15,38391,38392],{},"Keeping your groups compact will save time when reading the flow. This is especially helpful if when viewed on a smaller screen.",[1165,38394,38396],{"id":38395},"consider-how-hard-a-flow-made-of-groups-spaced-out-like-this-would-be-to-read-on-a-smaller-laptop-screen","Consider how hard a flow made of groups spaced out like this would be to read on a smaller laptop screen.",[15,38398,38399],{},[165,38400],{"alt":38401,"src":38402,"title":38403},"\"Screenshot showing an example of flow with widely spaced groups\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fcompact-bad.png","Screenshot showing an example of flow with widely spaced groups",[1165,38405,38407],{"id":38406},"this-now-takes-up-less-space-and-is-arguably-easier-to-read-on-any-screen-size","This now takes up less space and is arguably easier to read on any screen size.",[15,38409,38410],{},[165,38411],{"alt":38412,"src":38413,"title":38414},"\"Screenshot showing an example of flow with compact groups\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fcompact-good.png","Screenshot showing an example of flow with compact groups",[399,38416,38418,38419,38423],{"id":38417},"dont-cross-beams-wires","Don’t cross ",[38420,38421,38422],"del",{},"beams"," wires",[15,38425,38426],{},"Crossed wires are not only hard to read, they can lead to misinterpretation of what a flow actually does. Where possible don’t cross your wires, where you can’t avoid it try to make sure it’s easy for the reader to understand where wires cross as rather than join.",[1165,38428,38430],{"id":38429},"this-is-confusing-which-change-node-does-the-top-switch-output-link-to","This is confusing, which change node does the top switch output link to?",[15,38432,38433],{},[165,38434],{"alt":38435,"src":38436,"title":38437},"\"Screenshot showing an example of the flow with nodes having crossed beams\u002Fwires\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fwires-bad.png","Screenshot showing an example of the flow with nodes having crossed beams\u002Fwires",[1165,38439,38441],{"id":38440},"this-is-better-much-less-chance-of-confusing-the-change-nodes","This is better, much less chance of confusing the change nodes.",[15,38443,38444],{},[165,38445],{"alt":38446,"src":38447,"title":38448},"\"Screenshot showing an example of the flow with nodes having correctly linked beams\u002Fwires\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fwires-good.png","Screenshot showing an example of the flow with nodes having correctly linked beams\u002Fwires",[399,38450,38452],{"id":38451},"dont-use-link-nodes-in-groups-where-avoidable","Don’t use link nodes in groups where avoidable",[15,38454,38455],{},"Excessive link nodes within groups can make a flow much harder to understand, where possible use wires to join nodes within a group.",[1165,38457,38459],{"id":38458},"this-is-hard-to-read-and-you-will-end-up-checking-the-link-nodes-again-and-again","This is hard to read and you will end up checking the link nodes again and again.",[15,38461,38462],{},[165,38463],{"alt":38464,"src":38465,"title":38466},"\"Screenshot showing the example of flow having the uneccessary link nodes\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fgroupwires-bad.png","Screenshot showing the example of flow having the uneccessary link nodes",[1165,38468,38470],{"id":38469},"functionally-identical-to-the-example-above-it-should-only-take-a-few-seconds-to-understand-this-flow-now","Functionally identical to the example above, it should only take a few seconds to understand this flow now.",[15,38472,38473],{},[165,38474],{"alt":38475,"src":38476,"title":38477},"\"Screenshot showing an example of the flow with the avoided link nodes\"","\u002Fblog\u002F2022\u002F12\u002Fimages\u002Fgroupwires-good.png","Screenshot showing an example of the flow with the avoided link nodes",[399,38479,38481],{"id":38480},"boost-collaboration-with-flowfuse","Boost Collaboration with FlowFuse",[15,38483,38484,38486,38487,29],{},[63,38485,803],{"href":810}," is a cloud-based platform that makes working together on Node-RED projects easier and more efficient. It’s trusted by industries like manufacturing and smart building management, as well as textiles, to improve their systems. For more information, refer to our ",[63,38488,38490],{"href":38489},"\u002Fcustomer-stories\u002F","customer stories",[15,38492,38493,38494,38498,38499,38504,38505,38509],{},"With FlowFuse, you can quickly ",[63,38495,38497],{"href":38496},"\u002Fdocs\u002Fuser\u002Fteam\u002F","set up and manage teams",", giving each member the right level of access. It keeps all your ",[63,38500,38503],{"href":38501,"rel":38502},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=KOnQnR7yfT0&list=PLpcyqc7kNgp3nRacWBJ9JUVUJqtTjXdvh&index=2",[67],"Node-RED instances organized in one place",", so your team can collaborate seamlessly. Plus, it features ",[63,38506,23707],{"href":38507,"rel":38508},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=m2Onip4Lf4w",[67],", which let you restore previous versions of your flows if something goes wrong. FlowFuse simplifies team collaboration, making it easier to manage and work on Node-RED projects.",[15,38511,38512],{},[19,38513,38514],{},"[Sign up]({% include \"sign-up-url.njk\" %}) now for a free trial and experience FlowFuse's powerful collaboration tools!",[399,38516,1425],{"id":1424},[15,38518,38519],{},"Working on Node-RED flows as part of a team doesn't have to be a headache. By following some simple best practices you can make collaboration smooth sailing for everyone involved. So next time you're starting work on a new Node-RED flow, remember these tips and make life easier for yourself and your teammates.",{"title":103,"searchDepth":104,"depth":104,"links":38521},[38522,38526,38530,38534,38538,38542,38547,38551,38552],{"id":38266,"depth":294,"text":38267,"children":38523},[38524,38525],{"id":38279,"depth":104,"text":38280},{"id":38290,"depth":104,"text":38291},{"id":38301,"depth":294,"text":38302,"children":38527},[38528,38529],{"id":38308,"depth":104,"text":38309},{"id":38319,"depth":104,"text":38320},{"id":38330,"depth":294,"text":38331,"children":38531},[38532,38533],{"id":38337,"depth":104,"text":38338},{"id":38348,"depth":104,"text":38349},{"id":38359,"depth":294,"text":38360,"children":38535},[38536,38537],{"id":38366,"depth":104,"text":38367},{"id":38377,"depth":104,"text":38378},{"id":38388,"depth":294,"text":38389,"children":38539},[38540,38541],{"id":38395,"depth":104,"text":38396},{"id":38406,"depth":104,"text":38407},{"id":38417,"depth":294,"text":38543,"children":38544},"Don’t cross beams wires",[38545,38546],{"id":38429,"depth":104,"text":38430},{"id":38440,"depth":104,"text":38441},{"id":38451,"depth":294,"text":38452,"children":38548},[38549,38550],{"id":38458,"depth":104,"text":38459},{"id":38469,"depth":104,"text":38470},{"id":38480,"depth":294,"text":38481},{"id":1424,"depth":294,"text":1425},"2022-12-05","Learn best practices for formatting Node-RED flows to enhance team collaboration. From descriptive group names to clear switch explanations, optimize your flows for readability and efficiency.",{"excerpt":38556},{"type":12,"value":38557},[38558],[15,38559,38260],{},{"title":38254,"description":38554},{"loc":37603},"blog\u002F2022\u002F12\u002Fnode-red-flow-best-practice","From formatting your flows for readability to providing clear comments on nodes and groups, a little bit of effort upfront can save your team a lot of headaches down the road",[8650,138,7457],"NMl9GCMXjWnUIs7QE2JKv88DWGjVTJx_uyZd3rhVUQ8",{"id":38567,"title":38568,"authors":38569,"body":38570,"cta":3,"date":38676,"description":38677,"extension":116,"image":3,"lastUpdated":3,"meta":38678,"navigation":129,"path":38683,"seo":38684,"sitemap":38685,"stem":38686,"subtitle":38687,"tags":38688,"tldr":3,"video":3,"__hash__":38689},"blog\u002Fblog\u002F2022\u002F11\u002Fscaling-node-red-with-diy-tooling.md","Challenges scaling Node-RED with DIY tooling",[2622],{"type":12,"value":38571,"toc":38669},[38572,38575,38578,38582,38590,38594,38601,38604,38608,38611,38614,38618,38624,38635,38644,38647,38649,38655,38662],[15,38573,38574],{},"In this post, I'm going to share some of the challenges customers face when\nscaling Node-RED with Do-It-Yourself tooling.",[15,38576,38577],{},"Specifically, we'll talk about\ncommon threads in their journey building their own tooling around Node-RED, its\nflows, and deploying them. Node-RED is a visual programming environment for\nwiring together hardware devices, APIs and online services in a single\napplication. It's great because it's flexible enough to be used by both\nbeginners and experts alike; however, going from one instance of Node-RED to 100\nisn't for the faint-hearted.",[399,38579,38581],{"id":38580},"zero-to-hero-in-a-few-days","Zero to hero in a few days",[15,38583,38584,38585,38589],{},"If you’re new to Node-RED and are just getting started, the first step is simple:\ngo to ",[63,38586,30441],{"href":38587,"rel":38588},"https:\u002F\u002Fnodered.org",[67],", download, install and run. There’s no\nneed to configure anything or set up credentials or security or alerts. You can\nget started with Node-RED straight away by simply running it on your machine.\nThe guides and scripts provided on Node-RED or are more than enough to get\nstarted. You'll dive right in and start developing your flows.",[399,38591,38593],{"id":38592},"onto-the-second-instance","Onto the second instance",[15,38595,38596,38597,38600],{},"The next instance is a simple copy of the first. You'll need to make sure that\nyou are installing the same version on both instances, and that any\nconfiguration files (such as the ",[924,38598,38599],{},"settings.js",") are also in sync.",[15,38602,38603],{},"The second instance improves the first equally. Docs are read again,\nimprovements are made, and copied over. Life's good! Although there's a slight\nitch to start automating the setup, it's ignored. Just too many open questions\non how to achieve it: write scripts in bash? Or can we use Node-RED to manage\nNode-RED? Automation can wait, there's new flows to implement!",[399,38605,38607],{"id":38606},"wake-up-call-how-many-of-them-do-we-have","Wake-up call; how many of them do we have?",[15,38609,38610],{},"There's nothing like a good ol' wake-up call to get you back on track. One of\nour team members asked a questions about a buggy flow you've got no recollection\nof. During the investigation there's issues left and right; it's running a much\nolder version, missing standard packages, the settings are out of date, and the\ntimezone not set to UTC? Adoption of Node-RED is going quite well, it's useful\nand effective. Gets the job done without fuss. But now there's toil in\nmaintaining them; ensure the right tooling is build, properly documented, it\nneeds dashboards and overviews, lots of work to be done. Not quite business\nrelated, but Node-RED is important for the company, the bosses sure would\napprove spending 2 months building these tools!",[15,38612,38613],{},"But then something happened, there was a higher priority project to be picked up.\nSome tooling could be written for a couple of hours a month, but not two\ndedicated months to get it in tip top shape. Better than nothing! Built a\ndashboard in Node-RED to keep track of all devices, there's some scripts and a\nfew flows that aid in monitoring and maintenance. Scaling further is possible,\nbut confidence in the tooling isn't sky high.",[399,38615,38617],{"id":38616},"data-extraction-at-scale-now-theres-over-100","Data extraction at scale; now there's over 100",[15,38619,38620,38621,38623],{},"At ",[63,38622,803],{"href":810}," we've got regular conversations with customers managing 100s of\ndevices. Scaling to that many devices and runtimes requires hours of development\neach week alongside monitoring, maintenance, and auditing.",[15,38625,38626,38627,38630,38631],{},"As a side effect of investing more time into Node-RED and its ecosystem the\norganization has developed a few standards. Standard custom nodes that are\npre-installed (👋 ",[924,38628,38629],{},"moment.js","), a style guide published for developing flows,\nmaybe even flow linting: ",[63,38632,38633],{"href":38633,"rel":38634},"https:\u002F\u002Fgithub.com\u002Fnode-red\u002Fnrlint",[67],[15,38636,38637,38638,38643],{},"The security model is fairly decent. One just hopes the\n",[63,38639,38642],{"href":38640,"rel":38641},"https:\u002F\u002Fnl.wikipedia.org\u002Fwiki\u002FChief_Information_Security_Officer",[67],"CISO"," doesn't\ninspects them, but it's a fair bet they won't; we're far away from the\nheadquarters, right?",[15,38645,38646],{},"Many other edge cases itch in the back of our heads, but we can't focus on those right now.",[399,38648,1425],{"id":1424},[15,38650,38651,38652,38654],{},"Node-RED is a great tool, it's got many built-in features that make it easy to\nget started with no coding experience necessary. Running it at scale, in a\nproduction environment can require a lot of sys-ops and dev-ops time and we\nthink ",[63,38653,803],{"href":810}," is a great solution to keep that admin and tech debt in check.",[15,38656,38657,38658,29],{},"If you're running into these challenges we believe we can help, you can adopt\nour ",[63,38659,38661],{"href":38660},"\u002Fdocs\u002Finstall\u002F","free and open source edition",[15,38663,38664,38665,38668],{},"Additionally, to get a head start or enhance your current setup, explore our ",[63,38666,38667],{"href":25123},"Beginner's Guide to Professional Node-RED",". This comprehensive ebook offers a clear overview of Node-RED’s capabilities and practical tips for making the most of its features.",{"title":103,"searchDepth":104,"depth":104,"links":38670},[38671,38672,38673,38674,38675],{"id":38580,"depth":294,"text":38581},{"id":38592,"depth":294,"text":38593},{"id":38606,"depth":294,"text":38607},{"id":38616,"depth":294,"text":38617},{"id":1424,"depth":294,"text":1425},"2022-11-30","In this post, I'm going to share some of the challenges customers face when scaling Node-RED with Do-It-Yourself tooling",{"excerpt":38679},{"type":12,"value":38680},[38681],[15,38682,38574],{},"\u002Fblog\u002F2022\u002F11\u002Fscaling-node-red-with-diy-tooling",{"title":38568,"description":38677},{"loc":38683},"blog\u002F2022\u002F11\u002Fscaling-node-red-with-diy-tooling","Node-RED is very easy to get up and running for your first instance but what about your 100th?",[8650,138,7457],"HGhIh7rFl0dqN1uJQUp0ZRTepzg6rGqGXsuqF4gY6-w",{"id":38691,"title":38692,"authors":38693,"body":38695,"cta":3,"date":38738,"description":38739,"extension":116,"image":3,"lastUpdated":3,"meta":38740,"navigation":129,"path":38745,"seo":38746,"sitemap":38747,"stem":38748,"subtitle":38739,"tags":38749,"tldr":3,"video":3,"__hash__":38750},"blog\u002Fblog\u002F2022\u002F05\u002Fnode-red-3-beta-stack.md","Node-RED 3.0 Beta Stack",[38694],"sam-machin",{"type":12,"value":38696,"toc":38736},[38697,38700,38708,38724,38730,38733],[15,38698,38699],{},"The first beta of Node-RED 3.0 is here and FlowFuse is ready for you to try it out.",[15,38701,38702,38703,38707],{},"When we released ",[63,38704,38706],{"href":38705},"\u002Fblog\u002F2022\u002F04\u002Fflowforge-04-released\u002F","FlowFuse 0.4"," last month we talked about allowing users to select the stack their project runs on.\nUntil now we've only offered one stack which has been the latest Node-RED release (2.2.2).",[15,38709,38710,38711,38716,38717,38720,38721,29],{},"Yesterday the first beta of Node-RED 3.0 was ",[63,38712,38715],{"href":38713,"rel":38714},"https:\u002F\u002Fdiscourse.nodered.org\u002Ft\u002Fnode-red-3-0-0-beta-1-released\u002F62124",[67],"released",", so as of today we have added a choice of stacks to FlowFuse Cloud. You can stick with the ",[170,38718,38719],{},"Default"," and use Node-RED 2.2.2 or if you want to try out the beta you can select ",[170,38722,38723],{},"Node-RED-3.0.0-beta-1",[15,38725,38726],{},[165,38727],{"alt":38728,"src":38729,"title":38728},"Selecting the beta Stack","\u002Fblog\u002F2022\u002F05\u002Fimages\u002Fbeta_stack.gif",[15,38731,38732],{},"FlowFuse is the best way to run multiple Node-RED instances at different versions. Beta releases are exciting to try out, but you don't want to risk your production applications with an early upgrade. FlowFuse makes it easy to create a new project to try things out.",[15,38734,38735],{},"We'll continue to update the stack choice with each beta when they are released.",{"title":103,"searchDepth":104,"depth":104,"links":38737},[],"2022-05-04","Try out the next major Node-RED release",{"excerpt":38741},{"type":12,"value":38742},[38743],[15,38744,38699],{},"\u002Fblog\u002F2022\u002F05\u002Fnode-red-3-beta-stack",{"title":38692,"description":38739},{"loc":38745},"blog\u002F2022\u002F05\u002Fnode-red-3-beta-stack",[8650,138,136],"8G3rraTlHUUthqLaIBer8L9nCfUCVl2nMDyOoqJsSWs",{"id":38752,"title":38753,"authors":38754,"body":38755,"cta":3,"date":38811,"description":38812,"extension":116,"image":3,"lastUpdated":3,"meta":38813,"navigation":129,"path":38829,"seo":38830,"sitemap":38831,"stem":38832,"subtitle":38812,"tags":38833,"tldr":3,"video":3,"__hash__":38834},"blog\u002Fblog\u002F2022\u002F02\u002Fuse-case-solar-afloat.md","Using Node-RED to keep Solar PV afloat",[2622],{"type":12,"value":38756,"toc":38809},[38757,38777,38780,38783,38786,38794,38797,38800,38803],[15,38758,38759,38764,38765,38770,38771,38776],{},[63,38760,38763],{"href":38761,"rel":38762},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fkrishnanravichandran\u002F",[67],"Krisnan Ravichandran"," works\nfor ",[63,38766,38769],{"href":38767,"rel":38768},"https:\u002F\u002Fwww.sbp.solar",[67],"spb sonne",", a engineering consultancy for renewable\nenergy. Within the company he is part of the engineering effort on the\n",[63,38772,38775],{"href":38773,"rel":38774},"https:\u002F\u002Fwww.sbp.de\u002Fen\u002Fnews\u002Fgoembhal-sbp-sonnes-pioneering-floating-pv-system\u002F",[67],"Gömbhal","\nproject, creating a floatation device for solar panels.",[15,38778,38779],{},"In this post, he shares his experiences of using Node-RED.",[15,38781,38782],{},"Currently there’s a prototype deployed in Hungary, while the company is located\nin Stuttgart: “We’re remotely monitoring the installation. There are over 40\nsensors that all connect to an ADAM-6717, Compact Intelligent Gateway. When the\ndata is acquired we leverage Node-RED flows that maintain the structure, monitor\nperformance, and provide reporting back to our offices.",[15,38784,38785],{},"Node-RED is embedded in the ADAM 6717, it was very new to me. I was already\nexperienced in programming, mainly Python, and within a month I felt very\ncomfortable and productive in Node-RED. Understanding programming is useful,\nthough not a necessity.",[15,38787,38788,38789,38793],{},"Building and improving flows I did on my own through trial-and-error, as well\nas a lot of times through help on the ",[63,38790,38792],{"href":30471,"rel":38791},[67],"Node-RED Forum",".\nThe community is helpful and welcoming to new users. Now I maintain multiple\nflows with very different purposes. Some track temperature,\nirradiation wind-speed, direction, tilt and wave height; to ensure the floating\nPV installation remains floating. Other sensors are connected to actuators\nthrough flows that control pressure.",[15,38795,38796],{},"We do have some challenges; the ADAM 6717 contained an older version of Node-RED.\nThis raised questions around security and maintenance, as our Node-RED version\nisn’t updated to a newer version in an easy manner. It also hampers training a\nbit because documentation might reference an API or node for a flow that’s just\nnot the same on older versions.",[15,38798,38799],{},"However, I’d choose Node-RED again, it’s well known as well as easy to learn.\nFurthermore I found it very versatile.”",[38801,38802],"hr",{},[15,38804,38805,38806,29],{},"Thanks to Krisnan for sharing his story. If you have a Node-RED story for us\nto share, please get in touch via ",[63,38807,38808],{"href":29138},"contact@flowfuse.com",{"title":103,"searchDepth":104,"depth":104,"links":38810},[],"2022-02-09","How spb sonne used Node-RED with a renewable energy solution",{"excerpt":38814},{"type":12,"value":38815},[38816,38827],[15,38817,38818,38764,38821,38770,38824,38776],{},[63,38819,38763],{"href":38761,"rel":38820},[67],[63,38822,38769],{"href":38767,"rel":38823},[67],[63,38825,38775],{"href":38773,"rel":38826},[67],[15,38828,38779],{},"\u002Fblog\u002F2022\u002F02\u002Fuse-case-solar-afloat",{"title":38753,"description":38812},{"loc":38829},"blog\u002F2022\u002F02\u002Fuse-case-solar-afloat",[8650,138,137],"p5dtulm41xisdYA9wSvrLF1vMFWgcrytMp1bPOVzP18",1785528669696]