FlowFuse2025-07-31T00:00:00Z../nopathsource/98e7df1a1dd220ec540da9341c77dc8d../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/FlowFuse 2.20: AI-Assisted Node-RED & New Database ServiceIntroducing FlowFuse Tables for data storage, Tables nodes for database querying, Smart Suggestions in the Node-RED editor, More Powerful Instances, Retrieval Augmented Generation Blueprint for building intelligent applications, and a redesigned Applications page for better workspace management.2025-07-31T00:00:00ZGreg Stoutenburg<p>This release represents a major leap forward in FlowFuse's data management and AI capabilities, introducing our new FlowFuse Tables database feature, along with enhanced AI assistance features and a streamlined user interface. These improvements make FlowFuse a complete solution for building industrial applications, even while reducing development time.</p>
<!--more-->
<h2 id="introducing%3A-flowfuse-tables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#introducing%3A-flowfuse-tables"></a> Introducing: FlowFuse Tables</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/tables-ui-screenshot-qqhYtBtIX1-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/tables-ui-screenshot-qqhYtBtIX1-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot of the new "Tables" view, now available in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/tables-ui-screenshot-qqhYtBtIX1-1920.jpeg" width="1920" height="1041" /></picture>
<em>A screenshot of the new "Tables" view, now available in FlowFuse</em></p>
<p>FlowFuse Tables is our brand new database offering that provides a simple way to store your data, all within the FlowFuse ecosystem. This comprehensive database offering comes with FlowFuse's enterprise-grade security and unlocks the ability to seamlessly build critical systems like MES and ERP.</p>
<p>FlowFuse Tables eliminates the complexity of setting up and managing separate database infrastructure, allowing you to focus on building applications that drive operational efficiency.</p>
<p>FlowFuse Tables is available now for all Enterprise users running on FlowFuse Cloud.</p>
<h3 id="new-node%3A-query" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#new-node%3A-query"></a> New Node: Query</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/tables-query-node-lR4rwQwZHV-1323.avif 1323w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/tables-query-node-lR4rwQwZHV-1323.webp 1323w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A flow in Node-RED that uses the new FlowFuse "Query" node" loading="lazy" decoding="async" src="https://flowfuse.com/img/tables-query-node-lR4rwQwZHV-1323.jpeg" width="1323" height="944" /></picture>
<em>A flow in Node-RED that uses the new FlowFuse "Query" node</em></p>
<p>Alongside the new Tables offering, we have shipped a new node that you can find in your Node-RED Editor - "Query". This will automatically connect to any associated database you have with your team, saving you time in manually configuring nodes and credentials, giving you more time to just focus on the fun of building your flows, and making it really easy to start storing and querying your data.</p>
<h2 id="ai-assisted-node-red-with-smart-suggestions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#ai-assisted-node-red-with-smart-suggestions"></a> AI-Assisted Node-RED with Smart Suggestions</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/smart-suggestion-ewa3JozLIx-1139.gif 1139w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="GIF of Smart Suggestions" loading="lazy" decoding="async" src="https://flowfuse.com/img/smart-suggestion-ewa3JozLIx-1139.webp" width="1139" height="702" /></picture>
<em>GIF of Smart Suggestions in Action</em></p>
<p>Development in Node-RED is now even faster with Smart Suggestions, an agent that runs in-browser and offers intelligent flow completion for next-node recommendations. With Smart Suggestions, as you place a node, the agent will automatically calculate the most likely next node to place, and will offer suggestions for the node's configuration. It present up to 5 options, so even if the first suggestion isn't correct, it's very likely that the correct choice is only a quick keyboard shortcut away.</p>
<p>This work extends the functionality of the in-built FlowFuse Assistant and it's MCP server that runs behind the Node-RED Editor to provide power additional development enhancements.</p>
<h2 id="new-blueprint%3A-agentic-ai-with-retrieval-augmented-generation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#new-blueprint%3A-agentic-ai-with-retrieval-augmented-generation"></a> New Blueprint: Agentic AI with Retrieval Augmented Generation</h2>
<p><video src="https://website-data.s3.eu-west-1.amazonaws.com/Blueprint+-+Open+AI+RAG.mp4" controls=""></video></p>
<p>The new RAG (Retrieval Augmented Generation) Blueprint enables you to train your own LLM agents, combining your own data with natural language capabilities.</p>
<p>This Blueprint provides two flows: one that adds text into Node-RED's flow context store and uses it to train an OpenAI agent, so you can query the content of the flow directly; and one flow that scrapes websites to train an OpenAI agent so that content can be queried and used as well.</p>
<p>The RAG Blueprint makes it easy to create intelligent agents that leverage your organizational knowledge without requiring deep AI expertise. <a href="https://flowfuse.com/blueprints/ai/rag-chat-agent/">Try it out for yourself here.</a></p>
<h2 id="refined-applications-page" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#refined-applications-page"></a> Refined Applications Page</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/applications-redesign-7YfsdGCCdJ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/applications-redesign-7YfsdGCCdJ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of New Applications Page" loading="lazy" decoding="async" src="https://flowfuse.com/img/applications-redesign-7YfsdGCCdJ-1920.jpeg" width="1920" height="1065" /></picture>
<em>Screenshot of Redesigned Applications Page</em></p>
<p>With the new FlowFuse Home page in place, we have greatly streamlined the Applications page. The new structure includes:</p>
<ul>
<li><strong>Streamlined Navigation</strong>: Applications now appear under Instances in the navigation hierarchy</li>
<li><strong>Reduced Cognitive Load</strong>: Eliminated the overwhelming number of buttons and links in the previous design</li>
<li><strong>Focus on Important Information</strong>: The newly refined design focusses on giving you a clear overview of the status of your Hosted and Remote Instances, split by Application.</li>
<li><strong>Performance Optimizations</strong>: The above has also lead to faster page loading and improved responsiveness</li>
</ul>
<p>This redesign creates a more intuitive workflow that aligns with how teams actually use FlowFuse, reducing clicks and improving productivity.</p>
<h2 id="more-powerful-%22small%22-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#more-powerful-%22small%22-instances"></a> More Powerful "Small" Instances</h2>
<p>Based on user feedback and our own review of instance performance, we have increased the CPU and memory of all "small" Node-RED instances running on FlowFuse. This will have the immediate benefit of preventing slowdowns and loading issues for all Starter and Team customers.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#what's-next%3F"></a> What's Next?</h2>
<p>Our development roadmap continues to focus on AI integration and enterprise data management. Upcoming releases will expand FlowFuse Tables with additional database types and analytics capabilities, while our AI Assistant will gain more sophisticated workflow automation features.</p>
<p>We're also working on enhanced Blueprint offerings and deeper integration between our AI capabilities and industrial data sources, with several exciting announcements planned for the coming months!</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#what-else-is-new%3F"></a> What else is new?</h2>
<p>For a complete list of everything included in our 2.20 release, check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.20.0">release notes</a>.</p>
<p>Your feedback continues to be invaluable in shaping FlowFuse's development. We'd love to hear your thoughts on these new features and any suggestions for future improvements. Please share your experiences or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Which of these new features are you most excited to try? Email me directly at greg@flowfuse.com - I'd love to hear from you!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest way to get started is with FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> and have your Node-RED instances running in the cloud within minutes.</p>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-20/#self-hosted"></a> Self-Hosted</h3>
<p>Get FlowFuse running locally in under 30 minutes using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/FlowFuse AI Assistant: Let Your Engineers Build Automation, Not Write CodeMake Node-RED do more without writing code.2025-07-29T00:00:00ZSumit Shinde<p>Every manufacturing engineer knows this scenario: Node-RED's visual programming handles most of your automation needs brilliantly. Connect to PLCs, route data, trigger actions—all with drag-and-drop simplicity, but then you hit the wall. Your machine outputs data in a proprietary format. You need a custom dashboard widget that doesn't exist. You're manually creating test data for hours, or you're trying to understand a complex flow built by someone who left last year.</p>
<!--more-->
<p>These bespoke tasks can often demand coding skills—JavaScript for parsing data, Vue.js for custom widgets, CSS for styling. Skills your automation engineers might not have. Skills that pull them away from what the engineers do best: optimizing production.</p>
<p>FlowFuse's AI Assistant changes this dynamic completely. Describe what you need in plain English, then, get working code instantly. No more hours lost to syntax errors or Stack Overflow searches. Your engineers can focus on their own expertise, and FlowFuse Assistant fills in the gaps. Let's look at how manufacturing teams can use it to solve real problems.</p>
<h2 id="parsing-machine-data-without-the-javascript-struggle" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/#parsing-machine-data-without-the-javascript-struggle"></a> Parsing Machine Data Without the JavaScript Struggle</h2>
<p>While Node-RED's low-code nodes cover most scenarios, every production line has unique quirks. Complex data parsing, multi-step calculations, proprietary protocols—these can all be accomplished in Node-RED by writing your own JavaScript. Engineers, who should be focussed on optimizing processes, end up out of their comfort zone, searching regex patterns and debugging syntax errors.</p>
<p>Consider this output from a CNC machine that's been reliable for 15 years:</p>
<div style="position: relative" id="code-container-19">
<pre class="language-text"><code id="code-19" class="language-text">-- CYCLE END REPORT --<br />ID: M-45B / PART: XF-201<br />TIMESTAMP: 2025-07-09T14:22:01Z<br />SERIAL: 2025-0001547<br />OPERATOR: JONES, M<br />STATS ---<br />PART_COUNT: 481<br />CYCLE_TIME (S): 114.72<br />MAX_TEMP (C): 85.3<br />TOOL_WEAR_IDX: 0.73<br />COOLANT_LEVEL: OK<br />ALERTS: NONE<br />-- END --</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-19" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The traditional approach means spending 30-50 minutes writing regex patterns, handling edge cases, and testing thoroughly. Your engineer needs to remember JavaScript string methods, debug regex syntax, and account for variations in the output format.</p>
<p>With FlowFuse Assistant, the process changes completely. Your engineer opens a function node, clicks the FlowFuse Assistant button, and types:</p>
<blockquote>
<p>"Parse this CNC report format. Extract PART_COUNT as integer, CYCLE_TIME as float, TOOL_WEAR_IDX as float, OPERATOR name, and ALERTS. Return as JSON."</p>
</blockquote>
<p>The engineer reviews the code, tests it with their data, and moves on to solving actual manufacturing problems instead of wrestling with JavaScript syntax.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/function-ai-UUAYEyItf4-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse AI Assistant generating a Node-RED function node to extract data from a CNC text report." loading="lazy" decoding="async" src="https://flowfuse.com/img/function-ai-UUAYEyItf4-800.webp" width="800" height="347" /></picture>
<em>FlowFuse AI Assistant generating a Node-RED function node to extract data from a CNC text report.</em></p>
<h2 id="creating-test-data-in-seconds%2C-not-hours" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/#creating-test-data-in-seconds%2C-not-hours"></a> Creating Test Data in Seconds, Not Hours</h2>
<p>Before connecting to live equipment, you need test data that looks real. Making it by hand is tedious and wastes lots of time.</p>
<p>With FlowFuse Assistant, just ask:</p>
<blockquote>
<p>"Generate 20 machine records with machine_id, production_count, efficiency_percentage, downtime_minutes, and last_maintenance_date. Show realistic variations."</p>
</blockquote>
<p>It's as simple as that.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/json-ai-BTkatKYdOa-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse AI Assistant generating test JSON data with multiple machine records including production counts and efficiency metrics" loading="lazy" decoding="async" src="https://flowfuse.com/img/json-ai-BTkatKYdOa-800.webp" width="800" height="433" /></picture>
<em>FlowFuse AI Assistant creating realistic test data for manufacturing dashboards—complete with machine IDs, production metrics, and maintenance dates.</em></p>
<h2 id="building-custom-dashboards-without-web-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/#building-custom-dashboards-without-web-development"></a> Building Custom Dashboards Without Web Development</h2>
<p><a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a> widgets cover most UI needs, but manufacturing can often demand more. Custom visualizations for specific KPIs can be build in Dashboard with the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template">"Template"</a> node, where developers can write their own Vue.js templates.</p>
<p>Developing custom components to match your HMI design standards takes CSS expertise. Whether you're building new widgets or styling existing ones, you're suddenly in web development territory—far from where most automation engineers want to be.</p>
<p>Take a <a href="https://en.wikipedia.org/wiki/Pareto_chart">Pareto Chart</a> for defect analysis as an example—essential for quality teams but not available as a standard Dashboard widget. Building it requires Vue.js knowledge, Chart.js integration, and responsive design skills.</p>
<p>With FlowFuse's AI Assistant, just describe what you need:</p>
<blockquote>
<p>"Create a Pareto chart widget showing defect counts as bars with a cumulative percentage line. Include the 80% threshold."</p>
</blockquote>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/dashboard-ai-EVERidNHX4-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse AI Assistant creating custom dashboard components and styling for manufacturing displays" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-ai-EVERidNHX4-800.webp" width="800" height="347" /></picture>
<em>FlowFuse AI Assistant building both custom widgets and styling existing components for manufacturing dashboards.</em></p>
<p>Or consider input boxes for operator data entry that look too modern. Your team prefers the familiar green LCD screens they've used for decades, you can ask FlowFuse Assistant:</p>
<blockquote>
<p>"Add CSS that makes the input with classes 'calculator' and 'text-input' look like an old green LCD calculator screen."</p>
</blockquote>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/ai-css-C2ad_eAsUn-1089.gif 1089w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse AI Assistant generating CSS to style input fields with green LCD calculator display appearance" loading="lazy" decoding="async" src="https://flowfuse.com/img/ai-css-C2ad_eAsUn-1089.webp" width="1089" height="790" /></picture>
<em>AI Assistant creating CSS that transforms standard input boxes into retro LCD displays with glowing green text.</em></p>
<p>Whether creating new widgets or styling existing ones, AI Assistant handles the Vue.js and CSS complexity. You describe the outcome—it generates the code.</p>
<h2 id="documenting-complex-flows-before-knowledge-walks-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/#documenting-complex-flows-before-knowledge-walks-out"></a> Documenting Complex Flows Before Knowledge Walks Out</h2>
<p>Production flows evolve over years into complex systems. Hundreds of nodes, critical logic buried in functions, intricate routing between tabs. When knowledge walks out the door, new team members can face weeks of detective work trying to understand flows and their colleague's work.</p>
<p>FlowFuse Assistant's Flow Explainer solves this. Select any flow or group of nodes, click "Explain," and get instant documentation. It analyzes connections, reads function code, and generates clear explanations of what everything does and why.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/flow-expainer-ai-7rT7Pl0Af6-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse AI Assistant explaining the purpose and behavior of a complex Node-RED flow in plain language." loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-expainer-ai-7rT7Pl0Af6-800.webp" width="800" height="347" /></picture>
<em>FlowFuse AI Assistant turning complex flows into clear documentation for easy knowledge transfer.</em></p>
<h2 id="start-building-today" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-ai-assistant-better-node-red-manufacturing/#start-building-today"></a> Start Building Today</h2>
<p>FlowFuse Assistant is just one way FlowFuse helps manufacturing teams work faster.</p>
<p>Teams collaborate on flows in real-time. Version control with snapshots means you can always go back if something breaks. Remote device management handles edge devices across your factory floor. Deploy to one machine or a thousand with a single click. Built-in DevOps pipelines streamline your workflow from development to production.</p>
<p>For production reliability, high availability keeps systems running 24/7. The integrated MQTT broker handles all your device messaging. Enterprise security includes SSO, multi-factor authentication, role-based access control, and complete audit logs.</p>
<p>FlowFuse gives you everything you need to build, deploy, and manage Node-RED at scale.</p>
<p><a href="https://app.flowfuse.com/account/create">Try FlowFuse free →</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/How Leading Manufacturers Save Thousands of Dollars/Year with Real-Time SPCA practical guide to implementing Statistical Process Control with FlowFuse2025-07-24T00:00:00ZSumit ShindeSteve McLaughlin<p>Leading manufacturers are quietly saving thousands, sometimes millions, of dollars annually with a quality control method that's been proven since the 1920s. The difference today? Modern tools make it simple to implement.</p>
<!--more-->
<p>Consider this real example: A manufacturer shared on Practical Machinist forum that they process 400,000 parts per year with a 4% scrap rate. That's 16,000 parts discarded annually. At even a conservative $10 per part, this represents $160,000 in direct losses from a single production line. They accepted this as normal because industry reports confirm 4-5% scrap rates are standard.</p>
<p>What separates industry leaders from the rest? They refuse to accept "normal" waste. Using Statistical Process Control (SPC), they detect problems as they occur, not after producing defective parts. When a process begins drifting, they receive immediate alerts and correct it before generating scrap.</p>
<p>This guide shows you exactly how to build a real-time SPC system using FlowFuse. You'll create a live dashboard that tracks measurements and alerts operators the moment something goes wrong. No statistics degree needed, just practical steps you can implement today.</p>
<h2 id="why-traditional-quality-control-falls-short" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#why-traditional-quality-control-falls-short"></a> Why Traditional Quality Control Falls Short</h2>
<p>Most manufacturers still rely on end-of-line inspection. Make parts, check parts, scrap the bad ones. This reactive approach creates three expensive problems:</p>
<p><strong>Problem 1: You're always too late</strong>
When inspection finds a defect, you've already invested in material, machine time, labor, and energy. That investment is now scrap. Worse, how many parts did you make between when the problem started and when you caught it?</p>
<p><strong>Problem 2: The borderline parts you miss</strong>
Not all defects are obvious. Parts that barely pass inspection today might fail in the field tomorrow. These marginal parts slip through because traditional inspection only catches clear failures, not process degradation.</p>
<p><strong>Problem 3: No insight into root causes</strong>
Finding bad parts tells you nothing about why they're bad. Was it temperature drift? Tool wear? Material variation? Without process data, you're guessing at solutions.</p>
<p><strong>The Proactive Alternative: Statistical Process Control</strong></p>
<p>SPC flips the entire approach. Instead of checking parts after production, it monitors your process during production. The NIST Engineering Statistics Handbook explains it simply:</p>
<blockquote>
<p>"The underlying concept of statistical process control is based on a comparison of what is happening today with what happened previously."</p>
</blockquote>
<p>When your process starts to drift from its normal behavior, SPC alerts you immediately. You fix the issue before making defective parts, not after.</p>
<p>Walter Shewhart developed this method at Bell Labs in the 1920s, proving its effectiveness across industries. Today, FlowFuse makes it accessible to any manufacturer, regardless of size or technical expertise.</p>
<h2 id="building-your-first-spc-system-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#building-your-first-spc-system-with-flowfuse"></a> Building Your First SPC System with FlowFuse</h2>
<p>Let's build a real example. This guide shows you how to create a SPC control chart for individual measurements - perfect for monitoring critical dimensions in real-time.</p>
<p>First, open your FlowFuse Node-RED instance. If you don't have one yet, you can <a href="https://app.flowfuse.com/account/create">sign up for a free account</a> and have an instance running in minutes.</p>
<h3 id="what-we're-building" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#what-we're-building"></a> What We're Building</h3>
<p>A real-time SPC control chart that monitors individual measurements and automatically calculates control limits. Perfect for scenarios where you measure one part at a time - like bearing dimensions on a CNC line. When measurements drift outside the calculated limits, you'll know instantly.</p>
<h3 id="step-1%3A-get-your-tools-ready" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-1%3A-get-your-tools-ready"></a> Step 1: Get Your Tools Ready</h3>
<p>First, we need two nodes for SPC monitoring.</p>
<ol>
<li>
<p>Open FlowFuse's palette manager (hamburger menu → Manage palette)</p>
</li>
<li>
<p>Go to the Install tab</p>
</li>
<li>
<p>Search for and install these nodes:</p>
<ul>
<li><code>node-red-contrib-simple-spc</code> - This is your statistics engine</li>
<li><code>@flowfuse/node-red-dashboard</code> - For that slick real-time chart</li>
</ul>
</li>
<li>
<p>Click Install for each node and wait for completion</p>
</li>
</ol>
<h3 id="step-2%3A-simulate-your-machine-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-2%3A-simulate-your-machine-data"></a> Step 2: Simulate Your Machine Data</h3>
<p>In production, you'd connect to your PLC. For now, let's simulate a bearing measurement around 10mm nominal.</p>
<ol>
<li>
<p>Drag an <strong>inject</strong> node onto your canvas. This is your "sensor."</p>
</li>
<li>
<p>Double-click it and set:</p>
<ul>
<li>Repeat: <code>interval</code> → <code>2 seconds</code> (mimics real sensor timing)</li>
<li>Payload: switch to JSONata mode and enter: <code>$random() * 0.2 + 10</code></li>
</ul>
</li>
</ol>
<p>This generates readings like 10.05, 9.98, 10.11 - realistic variation around 10mm.</p>
<h3 id="step-3%3A-add-the-statistical-brain" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-3%3A-add-the-statistical-brain"></a> Step 3: Add the Statistical Brain</h3>
<p>Find the <strong>spc</strong> node in your palette (it'll be under "function" category after install).</p>
<ol>
<li>
<p>Drag it over and wire your inject node to it.</p>
</li>
<li>
<p>Double-click to configure:</p>
<ul>
<li>Control Limit Multiplier: <code>3</code> (for 3-sigma limits)</li>
<li>Timer: <code>10</code> (seconds before alerting out-of-control)</li>
</ul>
</li>
</ol>
<p>Once configured, the node outputs an object like this:</p>
<div style="position: relative" id="code-container-166">
<pre class="language-javascript"><code id="code-166" class="language-javascript"><span class="token punctuation">{</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">10.020214950604476</span><span class="token punctuation">,</span> <span class="token comment">// Current measurement</span><br /> <span class="token literal-property property">avg</span><span class="token operator">:</span> <span class="token number">10.089877376434453</span><span class="token punctuation">,</span> <span class="token comment">// Running average</span><br /> <span class="token literal-property property">ucl</span><span class="token operator">:</span> <span class="token number">10.344067494423967</span><span class="token punctuation">,</span> <span class="token comment">// Upper control limit</span><br /> <span class="token literal-property property">lcl</span><span class="token operator">:</span> <span class="token number">9.835687258444938</span><span class="token punctuation">,</span> <span class="token comment">// Lower control limit</span><br /> <span class="token literal-property property">outOfControl</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token comment">// Alert status</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-166" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="step-4%3A-make-the-data-chart-ready" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-4%3A-make-the-data-chart-ready"></a> Step 4: Make the Data Chart-Ready</h3>
<p>Charts need data in a specific format. Add a <strong>change</strong> node between SPC and your chart.</p>
<ol>
<li>
<p>Drag a <strong>change</strong> node onto the canvas</p>
</li>
<li>
<p>Wire it between the SPC node and where your chart will go</p>
</li>
<li>
<p>Double-click to configure and set one rule: <code>Set msg.payload</code> to this JSONata expression:</p>
</li>
</ol>
<div style="position: relative" id="code-container-190">
<pre class="language-json"><code id="code-190" class="language-json"><span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><span class="token property">"series"</span><span class="token operator">:</span> <span class="token string">"Measurement"</span><span class="token punctuation">,</span> <span class="token property">"x"</span><span class="token operator">:</span> $millis()<span class="token punctuation">,</span> <span class="token property">"y"</span><span class="token operator">:</span> payload.value<span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token property">"series"</span><span class="token operator">:</span> <span class="token string">"UCL"</span><span class="token punctuation">,</span> <span class="token property">"x"</span><span class="token operator">:</span> $millis()<span class="token punctuation">,</span> <span class="token property">"y"</span><span class="token operator">:</span> payload.ucl<span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token property">"series"</span><span class="token operator">:</span> <span class="token string">"LCL"</span><span class="token punctuation">,</span> <span class="token property">"x"</span><span class="token operator">:</span> $millis()<span class="token punctuation">,</span> <span class="token property">"y"</span><span class="token operator">:</span> payload.lcl<span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token property">"series"</span><span class="token operator">:</span> <span class="token string">"Average"</span><span class="token punctuation">,</span> <span class="token property">"x"</span><span class="token operator">:</span> $millis()<span class="token punctuation">,</span> <span class="token property">"y"</span><span class="token operator">:</span> payload.avg<span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-190" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This creates four lines on your chart, your actual measurements plus the three control lines.</p>
<h3 id="step-5%3A-build-your-control-room-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-5%3A-build-your-control-room-dashboard"></a> Step 5: Build Your Control Room Dashboard</h3>
<p>Now we'll create the dashboard that displays your SPC control chart. This is what operators will watch to spot problems in real-time.</p>
<ol>
<li>
<p>Drag a <strong>chart</strong> widget from the dashboard section and set:</p>
<ul>
<li>Type: <code>Line Chart</code></li>
<li>X-axis: <code>HH:mm:ss</code> (shows time)</li>
<li>Series: Set to <code>series</code> (as key)</li>
<li>X: Set to <code>x</code> (as key)</li>
<li>Y: Set to <code>y</code> (as key)</li>
<li>Legend: <code>Show</code></li>
<li>Label: "SPC Chart: Bearing Diameter (mm)"</li>
</ul>
</li>
<li>
<p>Connect the change node to it.</p>
</li>
<li>
<p>Deploy the flow.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/spc-ly9njuc8NY-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/spc-ly9njuc8NY-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED flow showing SPC monitoring setup with inject node, SPC calculations, data formatting, and dashboard chart configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/spc-ly9njuc8NY-1920.jpeg" width="1920" height="567" /></picture>
<em>Complete SPC monitoring flow with real-time chart display</em></p>
<h3 id="step-6%3A-add-the-alert-system" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-6%3A-add-the-alert-system"></a> Step 6: Add the Alert System</h3>
<p>A chart is nice, but you need immediate alerts. Here's the clever bit:</p>
<ol>
<li>
<p>Add a <strong>switch</strong> node connected to your SPC output</p>
</li>
<li>
<p>Set it to check <code>msg.payload.outOfControl</code></p>
</li>
<li>
<p>Create two outputs: true and false</p>
</li>
<li>
<p>Now add a <strong>text</strong> nodes from dashboard. Wire it to your switch outputs.</p>
</li>
</ol>
<p>For the "true" path, set the text to something attention-grabbing using change node:
<code>⚠️ PROCESS OUT OF CONTROL - CHECK MACHINE!</code></p>
<p>For the "false" path:
<code>✓ Process Stable</code></p>
<ol start="5">
<li>Deploy the flow.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/spc-visual-alert-F0Ibel7fuU-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/spc-visual-alert-F0Ibel7fuU-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED alert system with switch node: Process stable" loading="lazy" decoding="async" src="https://flowfuse.com/img/spc-visual-alert-F0Ibel7fuU-1920.jpeg" width="1920" height="646" /></picture>
<em>Node-RED alert system with switch node: Process stable</em></p>
<h3 id="step-7%3A-test-your-spc-system" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#step-7%3A-test-your-spc-system"></a> Step 7: Test Your SPC System</h3>
<p>Open your dashboard (switch to the Dashboard 2.0 tab → click Open Dashboard button on the top-right of the right sidebar).</p>
<p><strong>What you'll see initially:</strong></p>
<ul>
<li>First 20 measurements: Chart builds up baseline data</li>
<li>After 20 points: Control limits appear automatically</li>
<li>Blue line bouncing between red lines: Process is stable</li>
</ul>
<p>Let the system run for a minute to establish your baseline. The control limits aren't arbitrary - they're calculated from your actual process data.</p>
<p><strong>Now test it with a simulated process shift:</strong></p>
<ol>
<li>
<p>Double-click the Inject node</p>
</li>
<li>
<p>Change the formula to <code>$random() * 0.2 + 10.5</code> (this shifts the mean up by 0.5mm)</p>
</li>
<li>
<p>Click Done to close the node</p>
</li>
<li>
<p>Deploy only the modified nodes:</p>
<ul>
<li>Click the down arrow next to the <strong>Deploy</strong> button</li>
<li>Select <strong>Modified Nodes</strong></li>
<li>Click the <strong>Deploy</strong> button</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/modifed-nodes-deploy-IsHFigsmsR-1060.avif 1060w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/modifed-nodes-deploy-IsHFigsmsR-1060.webp 1060w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED deploy menu showing Modified Nodes option for selective deployment" loading="lazy" decoding="async" src="https://flowfuse.com/img/modifed-nodes-deploy-IsHFigsmsR-1060.jpeg" width="1060" height="588" /></picture>
<em>Deploy menu with Modified Nodes option for updating only changed nodes</em></p>
<p>Once done You will see the chart detect the change within seconds.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/spc-visual-alert2-IDxDcwq_2o-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/spc-visual-alert2-IDxDcwq_2o-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="SPC chart showing process shift with measurements exceeding upper control limit and alert triggered" loading="lazy" decoding="async" src="https://flowfuse.com/img/spc-visual-alert2-IDxDcwq_2o-1920.jpeg" width="1920" height="645" /></picture>
<em>SPC chart detecting process shift - measurements above UCL trigger immediate alert</em></p>
<div id="nr-flow-193" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow193 = "\n[{\"id\":\"124f531365b82e1c\",\"type\":\"group\",\"z\":\"071524a8fd482116\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"4082f89605793666\",\"2ec8351f01f957a4\",\"c90d67c57d1af3bd\",\"53ef849a41aee2b3\",\"d78ff3dbe97a5be9\",\"98e76616c28ed9ca\",\"5fd3415ef4c36b6e\",\"9763632606c2a165\"],\"x\":214,\"y\":159,\"w\":872,\"h\":162},{\"id\":\"4082f89605793666\",\"type\":\"inject\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"2\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"$random() * 0.2 + 10.5\",\"payloadType\":\"jsonata\",\"x\":310,\"y\":240,\"wires\":[[\"2ec8351f01f957a4\"]]},{\"id\":\"2ec8351f01f957a4\",\"type\":\"spc\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"limitMultiplier\":\"3\",\"timer\":\"10\",\"x\":430,\"y\":240,\"wires\":[[\"c90d67c57d1af3bd\",\"d78ff3dbe97a5be9\"]]},{\"id\":\"c90d67c57d1af3bd\",\"type\":\"change\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[ {\\\"series\\\": \\\"Measurement\\\", \\\"x\\\": $millis(), \\\"y\\\": payload.value}, {\\\"series\\\": \\\"UCL\\\", \\\"x\\\": $millis(), \\\"y\\\": payload.ucl}, {\\\"series\\\": \\\"LCL\\\", \\\"x\\\": $millis(), \\\"y\\\": payload.lcl}, {\\\"series\\\": \\\"Average\\\", \\\"x\\\": $millis(), \\\"y\\\": payload.avg} ]\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":600,\"y\":200,\"wires\":[[\"53ef849a41aee2b3\"]]},{\"id\":\"53ef849a41aee2b3\",\"type\":\"ui-chart\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"group\":\"3af21cf2cea638c8\",\"name\":\"\",\"label\":\"I-MR Chart: Bearing Diameter (mm)\",\"order\":1,\"chartType\":\"line\",\"category\":\"series\",\"categoryType\":\"property\",\"xAxisLabel\":\"\",\"xAxisProperty\":\"x\",\"xAxisPropertyType\":\"property\",\"xAxisType\":\"time\",\"xAxisFormat\":\"\",\"xAxisFormatType\":\"auto\",\"xmin\":\"\",\"xmax\":\"\",\"yAxisLabel\":\"\",\"yAxisProperty\":\"y\",\"yAxisPropertyType\":\"property\",\"ymin\":\"\",\"ymax\":\"\",\"bins\":10,\"action\":\"append\",\"stackSeries\":false,\"pointShape\":\"circle\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":[\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#a347e1\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"textColor\":[\"#666666\"],\"textColorDefault\":true,\"gridColor\":[\"#e5e5e5\"],\"gridColorDefault\":true,\"width\":\"12\",\"height\":\"7\",\"className\":\"\",\"interpolation\":\"linear\",\"x\":840,\"y\":200,\"wires\":[[]]},{\"id\":\"d78ff3dbe97a5be9\",\"type\":\"switch\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"property\":\"payload.outOfControl\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"true\"},{\"t\":\"false\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":570,\"y\":260,\"wires\":[[\"98e76616c28ed9ca\"],[\"5fd3415ef4c36b6e\"]]},{\"id\":\"98e76616c28ed9ca\",\"type\":\"change\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"⚠️ PROCESS OUT OF CONTROL - CHECK MACHINE!\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":780,\"y\":240,\"wires\":[[\"9763632606c2a165\"]]},{\"id\":\"5fd3415ef4c36b6e\",\"type\":\"change\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"✓ Process Stable\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":780,\"y\":280,\"wires\":[[\"9763632606c2a165\"]]},{\"id\":\"9763632606c2a165\",\"type\":\"ui-text\",\"z\":\"071524a8fd482116\",\"g\":\"124f531365b82e1c\",\"group\":\"3af21cf2cea638c8\",\"order\":2,\"width\":0,\"height\":0,\"name\":\"Visual Alert\",\"label\":\"\",\"format\":\"\",\"layout\":\"col-center\",\"style\":true,\"font\":\"Arial,Arial,Helvetica,sans-serif\",\"fontSize\":\"24\",\"color\":\"#717171\",\"wrapText\":false,\"className\":\"\",\"x\":990,\"y\":260,\"wires\":[]},{\"id\":\"3af21cf2cea638c8\",\"type\":\"ui-group\",\"name\":\"Group 1\",\"page\":\"735102f229f1a45b\",\"width\":\"12\",\"height\":\"7\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"735102f229f1a45b\",\"type\":\"ui-page\",\"name\":\"SPC\",\"ui\":\"3c2e7c6f438349e4\",\"path\":\"/page1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"daff84b6f7fe1f97\",\"breakpoints\":[{\"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\":\"3c2e7c6f438349e4\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"showPathInSidebar\":false,\"headerContent\":\"page\",\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\",\"showReconnectNotification\":true,\"notificationDisplayTime\":1,\"showDisconnectNotification\":true,\"allowInstall\":true},{\"id\":\"daff84b6f7fe1f97\",\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow193.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-193') })</script>
<h2 id="spc-chart-rules-that-actually-matter" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#spc-chart-rules-that-actually-matter"></a> SPC Chart Rules That Actually Matter</h2>
<p>Control limits catch obvious problems. But subtle issues need pattern recognition. Here are the four rules that catch 95% of real problems:</p>
<ul>
<li>
<p><strong>Rule 1: Any point outside limits</strong>
This one is fairly obvious: If a measurement point falls outside of the established upper or lower limits, something is wrong.</p>
</li>
<li>
<p><strong>Rule 2: Seven points on one side</strong>
Seven consecutive points above or below the center line means your process has shifted. Maybe a new material lot, maybe tool wear.</p>
</li>
<li>
<p><strong>Rule 3: Seven points trending</strong>
Seven points in a row going up or down. Classic sign of tool wear or temperature drift.</p>
</li>
<li>
<p><strong>Rule 4: Repeating and Cyclical Patterns</strong>
This rule is for recurring behaviors that the other rules miss. Cycles that repeat over time, like a regular pattern that appears at the start of every shift change or every Monday. Speak with your operators, they will likely know what these mean.</p>
</li>
</ul>
<p>Here's how to implement these rules in FlowFuse:</p>
<ol>
<li>Drag a <strong>Function</strong> node onto the workspace and add the following JavaScript code to it.</li>
<li>Replace switch node with this <strong>Function</strong> node.</li>
<li>Deploy the flow.</li>
</ol>
<div style="position: relative" id="code-container-430">
<pre class="language-javascript"><code id="code-430" class="language-javascript"><span class="token keyword">const</span> buffer <span class="token operator">=</span> context<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'measurements'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> current <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">;</span><br /><br /><span class="token comment">// Rule 1: Out-of-control point (handled by SPC node)</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>outOfControl<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token string">"⚠️ PROCESS OUT OF CONTROL – CHECK MACHINE!"</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> msg<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Store the current measurement</span><br />buffer<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> current<span class="token punctuation">.</span>value<span class="token punctuation">,</span><br /> <span class="token literal-property property">avg</span><span class="token operator">:</span> current<span class="token punctuation">.</span>avg<span class="token punctuation">,</span><br /> <span class="token literal-property property">timestamp</span><span class="token operator">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Keep only the last 20 measurements</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>buffer<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">20</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> buffer<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br />context<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'measurements'</span><span class="token punctuation">,</span> buffer<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Initialize alert message</span><br /><span class="token keyword">let</span> alertMessage <span class="token operator">=</span> <span class="token string">"✓ Process Stable"</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Check rules if we have enough data</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>buffer<span class="token punctuation">.</span>length <span class="token operator">>=</span> <span class="token number">7</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> recent <span class="token operator">=</span> buffer<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> values <span class="token operator">=</span> recent<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">r</span> <span class="token operator">=></span> r<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Rule 2: Seven points on one side of the center line</span><br /> <span class="token keyword">const</span> allAbove <span class="token operator">=</span> values<span class="token punctuation">.</span><span class="token function">every</span><span class="token punctuation">(</span><span class="token parameter">v</span> <span class="token operator">=></span> v <span class="token operator">></span> current<span class="token punctuation">.</span>avg<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> allBelow <span class="token operator">=</span> values<span class="token punctuation">.</span><span class="token function">every</span><span class="token punctuation">(</span><span class="token parameter">v</span> <span class="token operator">=></span> v <span class="token operator"><</span> current<span class="token punctuation">.</span>avg<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>allAbove <span class="token operator">||</span> allBelow<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> alertMessage <span class="token operator">=</span> <span class="token string">"⚠️ Process shift detected – 7 points on one side of center"</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// Rule 3: Seven points trending up or down</span><br /> <span class="token keyword">let</span> trending <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> increasing <span class="token operator">=</span> values<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">></span> values<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> i <span class="token operator"><</span> values<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>increasing <span class="token operator">&&</span> values<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator"><=</span> values<span class="token punctuation">[</span>i <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">||</span><br /> <span class="token punctuation">(</span><span class="token operator">!</span>increasing <span class="token operator">&&</span> values<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">>=</span> values<span class="token punctuation">[</span>i <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> trending <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br /> <span class="token keyword">break</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>trending<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> alertMessage <span class="token operator">=</span> increasing<br /> <span class="token operator">?</span> <span class="token string">"⚠️ Increasing trend – check for tool wear"</span><br /> <span class="token operator">:</span> <span class="token string">"⚠️ Decreasing trend – check material quality"</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> alertMessage<span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-430" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The following image demonstrates the advanced alerting system in action, detecting specific patterns beyond simple control limit violations:</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/simulated-drift-alert-YCLPv2qHOP-400.gif 400w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="SPC system detecting process drift in real-time, showing chart responding to simulated measurement changes and triggering alerts" loading="lazy" decoding="async" src="https://flowfuse.com/img/simulated-drift-alert-YCLPv2qHOP-400.webp" width="400" height="227" /></picture>
<em>Real-time SPC monitoring detecting process drift and triggering appropriate alerts based on trend analysis</em></p>
<h2 id="connecting-to-real-equipment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#connecting-to-real-equipment"></a> Connecting to Real Equipment</h2>
<p>Time to connect your actual machines. The approach depends on what equipment you have.</p>
<p>For modern PLCs - anything from the last decade like Siemens S7-1200/1500, Allen-Bradley ControlLogix, or Omron NX - you'll use OPC UA. It's already built into these PLCs. Enable it in the configuration, install <code>node-red-contrib-opcua</code> from the FlowFuse palette, and point it at your PLC. The endpoint looks like <code>opc.tcp://192.168.1.100:4840</code>. Browse for your measurement tags and connect them to your SPC flow. <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/">Full OPC UA guide here</a>.</p>
<p>Older equipment speaks Modbus TCP. Check your manual's appendix for the register map. Install <code>node-red-contrib-modbus</code>, configure it with your device's IP address and the register holding your measurement (like 40001 for holding registers). Almost every industrial device from the last 30 years supports this. <a href="https://flowfuse.com/node-red/protocol/modbus/">Modbus tutorial here</a>.</p>
<p>For everything else, get creative. Old gauges with RS-232 ports work fine with a USB adapter and the serial node - <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/">see our serial port guide</a>. Machines that dump CSV files can be monitored with the watch node. Manual measurements need just a simple dashboard form, one input field, one submit button. Don't overcomplicate it.</p>
<p>Before connecting to SPC, always test with inject → protocol node → debug to make sure data flows. Once you see measurements in the debug panel, wire it to your SPC node and you're monitoring real processes.</p>
<h2 id="the-money-you're-leaving-on-the-table" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#the-money-you're-leaving-on-the-table"></a> The Money You're Leaving on the Table</h2>
<p>Remember that manufacturer from the forum processing 400,000 parts annually with a 4% scrap rate? That's 16,000 parts straight to the trash. At just $10 per part, that's $160,000 in annual waste. Cut that rate to 2% with SPC and you save $80,000 yearly.</p>
<p>But here's what most people miss - the $10 part cost is just the beginning. Consider what every defective part also burned through:</p>
<ul>
<li><strong>Machine time</strong>: Say 3 minutes at $200/hour = another $10 gone</li>
<li><strong>Labor</strong>: About 15 minutes handling the defect at $30/hour = $7.50 more</li>
<li><strong>Materials</strong>: The raw stock you'll never get back</li>
</ul>
<p>That "$10 part" actually cost you around $27.50 to scrap. Those 16,000 defects? Try $440,000 in real losses.</p>
<p>SPC attacks all of this simultaneously. When your process stays in control, you're not just saving parts - you're saving machine capacity, labor hours, and materials. Plus stable processes need less inspection, letting you redeploy quality staff to improvement projects instead of firefighting.</p>
<p><strong>Quick ROI calculation</strong>: Take your annual defect count, multiply by your true cost per defect (part + machine + labor), then multiply by 0.5 for a conservative estimate. That's your yearly savings potential with SPC. Most manufacturers see payback in under 3 months.</p>
<p>SPC works. It's not magic, it's not complicated, and it doesn't have to be expensive. It's just math applied to manufacturing data in real-time.</p>
<p>The tools exist. Node-RED gets you started, but FlowFuse keeps you running in production. With built-in high availability, your SPC charts stay live even if a server fails. Multiple engineers can work on the same flows without conflicts. Deploy updates to 50 production lines with one click. When downtime costs thousands per hour, you need a platform built for manufacturing.</p>
<h2 id="start-preventing-defects-today" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#start-preventing-defects-today"></a> Start Preventing Defects Today</h2>
<p>Every day without SPC is money left on the table. Those 16,000 scrapped parts per year? The warranty claims from undetected drift? All preventable.</p>
<p>Here's your path to production-ready SPC:</p>
<ol>
<li><strong><a href="https://app.flowfuse.com/account/create">Start Free with FlowFuse</a></strong> - Get your instance running in minutes</li>
<li><strong>Follow this tutorial</strong> - Build your first SPC chart today</li>
<li><strong>Connect one machine</strong> - Start with your most critical measurement</li>
<li><strong>Expand gradually</strong> - Add more parameters as you prove value</li>
</ol>
<p>Don't wait for the perfect plan. Don't form another committee. Pick one measurement that matters and start monitoring it today. Need help getting started? <a href="https://flowfuse.com/book-demo/">Book a demo</a> to discuss your specific requirements or <a href="https://flowfuse.com/contact-us/">contact our team</a> for enterprise deployment guidance.</p>
<p>Because somewhere right now, one of your machines is drifting out of spec. The only question is whether you'll catch it in time.</p>
<p><strong><a href="https://app.flowfuse.com/account/create">Build Your First SPC Dashboard Now →</a></strong></p>
<h2 id="references" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/quality-control-automation-spc-charts/#references"></a> References</h2>
<ol>
<li><a href="https://www.sixsigmaonline.org/six-sigma-statistical-process-control/">Six Sigma Online - Statistical Process Control</a> - Background on manufacturers saving thousands with quality control methods</li>
<li><a href="https://www.practicalmachinist.com/forum/threads/scrap-rates.234251/">Practical Machinist Forum - Scrap Rates Discussion</a> - Real manufacturer example: 400,000 parts/year with 4% scrap rate</li>
<li><a href="https://servicechannel.com/reports/scrap-rate/">ServiceChannel - Industry Scrap Rate Report</a> - Industry data confirming 4-5% scrap rates are standard</li>
<li><a href="https://www.itl.nist.gov/div898/handbook/pmc/section1/pmc12.htm">NIST Engineering Statistics Handbook - Statistical Process Control</a> - Official definition and explanation of SPC principles</li>
<li><a href="https://en.wikipedia.org/wiki/Walter_A._Shewhart">Walter Shewhart - Wikipedia</a> - SPC creator at Bell Labs in the 1920s</li>
</ol>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/OPC UA Tutorial: Connect and Exchange Data with Industrial EquipmentA practical guide to accessing industrial data through OPC UA server gateways2025-07-16T00:00:00ZSumit Shinde<p>If you’ve ever tried to connect industrial equipment from different vendors, you know how frustrating it can be, a mess of incompatible protocols, proprietary software, and confusing drivers. Your Siemens PLC speaks one language, your Allen-Bradley controller another, and that Modbus sensor? Yet another protocol entirely.</p>
<!--more-->
<p><strong>OPC UA changes that.</strong></p>
<p>OPC UA (Open Platform Communications Unified Architecture) is the industry-standard protocol that eliminates this chaos. Also known as OPC Unified Architecture or IEC 62541, it provides a universal language for secure communication between PLCs, SCADA systems, HMIs, and enterprise applications ,regardless of the manufacturer.</p>
<p>This hands-on guide walks you through building your first <strong>OPC UA integration</strong> using <strong>Node-RED</strong> and <strong>FlowFuse</strong>:</p>
<ul>
<li><strong>Connect</strong> to any OPC UA server—Kepware, MatrikonOPC, or built-in PLC servers</li>
<li><strong>Browse</strong> available tags and discover Node IDs from your equipment</li>
<li><strong>Read</strong> real-time values from PLCs, sensors, and industrial devices</li>
<li><strong>Write</strong> control signals and setpoints back to your systems</li>
</ul>
<h2 id="why-opc-ua%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#why-opc-ua%3F"></a> Why OPC UA?</h2>
<p>If you have worked with industrial equipment, you know the pain. Every PLC vendor uses a different protocol. Your Siemens S7-1500 requires TIA Portal and PROFINET drivers. The Allen-Bradley ControlLogix needs RSLinx and EtherNet/IP. A Modbus temperature sensor needs yet another tool. Before long, you are juggling a dozen different software packages—each with its own licensing, training, and maintenance overhead.</p>
<h3 id="breaking-the-cycle" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#breaking-the-cycle"></a> Breaking the Cycle</h3>
<p>OPC UA eliminates this fragmentation. Instead of relying on vendor-specific protocols, it provides a universal language for all your equipment. Here is why it is becoming the industry standard:</p>
<h3 id="universal-connectivity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#universal-connectivity"></a> Universal Connectivity</h3>
<p>Connect to any modern PLC using a single protocol. Leading manufacturers like Siemens, Rockwell, Schneider, and ABB now embed OPC UA servers directly into their controllers. One client, all your equipment.</p>
<h3 id="information%2C-not-just-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#information%2C-not-just-data"></a> Information, Not Just Data</h3>
<p>Reading a temperature value from OPC UA does not just give you "42.5"—it gives the full context: 42.5 °C, measured at 14:32:15.625 with "Good" quality, from "Tank_01/Temperature", and includes alarm limits (10 °C / 80 °C). This context reduces guesswork and helps prevent costly mistakes.</p>
<h3 id="security-built-for-industry" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#security-built-for-industry"></a> Security Built for Industry</h3>
<p>While protocols like Modbus transmit everything in plain text, OPC UA uses enterprise-grade security. It supports X.509 certificates, 256-bit encryption, and robust user authentication to safeguard critical infrastructure from cyber threats.</p>
<h3 id="future-proof-investment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#future-proof-investment"></a> Future-Proof Investment</h3>
<p>OPC UA is the foundation of Industry 4.0 initiatives around the world. It is not just another protocol—it is the one major vendors are standardizing on. Choosing OPC UA today ensures long-term compatibility and ROI.</p>
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#getting-started"></a> Getting Started</h2>
<p>Now that you understand why OPC UA is widely adopted, let’s explore how to implement it using FlowFuse Node-RED.</p>
<p>This next section walks you through exactly what you need to get started with a working setup, whether for prototyping or production.</p>
<h3 id="what-you%E2%80%99ll-need" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#what-you%E2%80%99ll-need"></a> What You’ll Need</h3>
<p>Before diving into the flow-building process, make sure you have the following:</p>
<ul>
<li>An OPC UA server (like Kepware, MatrikonOPC, or built into your PLC)</li>
<li>A FlowFuse Node-RED instance running on your edge device.</li>
</ul>
<p>For production OPC UA deployments, we recommend using FlowFuse. When connecting to industrial systems, you need more than just Node-RED—you need team collaboration so multiple engineers can work on flows safely, audit logs for compliance tracking, high availability to prevent downtime, and remote device management for edge deployments.</p>
<p>FlowFuse provides these enterprise features plus automatic backups, one-click rollbacks, environment variables for different sites, and DevOps pipelines for testing changes before they reach production.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started →</a></p>
<h3 id="installing-opc-ua-support-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#installing-opc-ua-support-in-flowfuse"></a> Installing OPC UA Support in FlowFuse</h3>
<p>To work with OPC UA in FlowFuse Node-RED, you will first need to install the required nodes.</p>
<h4 id="install-the-opc-ua-node-package" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#install-the-opc-ua-node-package"></a> Install the OPC UA Node Package</h4>
<ol>
<li>Open the <strong>FlowFuse Node-RED editor</strong>.</li>
<li>Click the menu in the top-right and choose <strong>Manage palette</strong>.</li>
<li>Navigate to the <strong>Install</strong> tab and search for <code>node-red-contrib-opcua</code>.</li>
<li>Click <strong>Install</strong>.</li>
</ol>
<p>Once installed, you will find new nodes for OPC UA communication in your palette, including <strong>Client</strong>, <strong>Item</strong>, and <strong>Browser</strong> and other OPC UA nodes.</p>
<h3 id="connecting-to-your-opc-ua-server" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#connecting-to-your-opc-ua-server"></a> Connecting to Your OPC UA Server</h3>
<p>To begin accessing industrial data, create a client connection using the OPC UA Client node.</p>
<ol>
<li>Drag an <strong>OPC UA Client</strong> node onto the canvas.</li>
<li>Double-click to configure it.</li>
<li>Click the <strong>+</strong> icon to create a new endpoint configuration.</li>
<li>Enter your OPC UA server address, for example: <code>opc.tcp://192.168.0.10:4840</code></li>
<li>Set the security mode to <strong>None</strong> (you can add security later).</li>
</ol>
<blockquote>
<p><strong>Security Note:</strong> This tutorial uses <strong>"None"</strong> for the security setting to keep things simple.
In production environments, always use appropriate security—typically <strong>"Sign & Encrypt"</strong> with certificates.</p>
</blockquote>
<ol start="6">
<li>Click <strong>Add</strong>, then <strong>Done</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-endpoint-config-5A59qK9PBD-1004.avif 1004w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-endpoint-config-5A59qK9PBD-1004.webp 1004w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OPC UA endpoint configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-endpoint-config-5A59qK9PBD-1004.jpeg" width="1004" height="736" /></picture>
<em>OPC UA endpoint configuration</em></p>
<p>With the connection now defined, you’re ready to explore what tags are available.</p>
<h3 id="browsing-tags-(optional)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#browsing-tags-(optional)"></a> Browsing Tags (Optional)</h3>
<p>If you do not already know the Node IDs of the tags you want to access, use the OPC UA Browser node to explore the tag structure.</p>
<ol>
<li>Drag an <strong>Inject</strong>, <strong>OPC UA Browser</strong>, and <strong>Debug</strong> node onto the canvas.</li>
<li>Connect the output of the Inject node to the input of the <strong>Browser</strong> node, then connect the Browser's output to the Debug node.</li>
<li>In the <strong>Browser</strong> node, set the topic to <code>ns=0;i=85</code> (the root <em>Objects</em> folder).</li>
<li>Configure the Inject node to send a timestamp.</li>
<li>Deploy the flow and click the Inject node.</li>
</ol>
<p>Tag information will be printed to the debug sidebar. You can now identify the exact Node IDs to use in your reads or writes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-browser-c8xOnhEc2i-1372.avif 1372w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-browser-c8xOnhEc2i-1372.webp 1372w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OPC UA Browser node" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-browser-c8xOnhEc2i-1372.jpeg" width="1372" height="646" /></picture>
<em>OPC UA Browser node</em></p>
<div id="nr-flow-188" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow188 = "\n[{\"id\":\"c3a8303048e6588f\",\"type\":\"OpcUa-Browser\",\"z\":\"f66e9c91c269e7fb\",\"endpoint\":\"c0f8c79fc00845c8\",\"item\":\"\",\"datatype\":\"\",\"topic\":\"ns=0;i=85\",\"items\":[],\"name\":\"\",\"x\":510,\"y\":300,\"wires\":[[\"3428199852f9fcdc\"]]},{\"id\":\"1549f797c58ba667\",\"type\":\"inject\",\"z\":\"f66e9c91c269e7fb\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":280,\"y\":300,\"wires\":[[\"c3a8303048e6588f\"]]},{\"id\":\"3428199852f9fcdc\",\"type\":\"debug\",\"z\":\"f66e9c91c269e7fb\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":740,\"y\":300,\"wires\":[]},{\"id\":\"c0f8c79fc00845c8\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow188.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-188') })</script>
<h3 id="reading-tag-values" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#reading-tag-values"></a> Reading Tag Values</h3>
<p>Once you know the Node IDs, you can start reading data from your industrial equipment through the OPC UA server.</p>
<h4 id="reading-a-single-tag" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#reading-a-single-tag"></a> Reading a Single Tag</h4>
<p>Here’s how to read a single value in real time:</p>
<ol>
<li>
<p>Drag an <strong>Inject</strong> node onto the canvas (this will trigger the read operation).</p>
</li>
<li>
<p>Add an <strong>OPC UA Item</strong> node and configure:</p>
<ul>
<li><strong>Node ID</strong>: Enter the tag’s identifier (e.g., <code>ns=3;i=1003</code>)</li>
<li><strong>Data Type</strong>: Select the appropriate type (e.g., <code>Boolean</code>)</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-item-node-W0_AiLIkYO-1008.avif 1008w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-item-node-W0_AiLIkYO-1008.webp 1008w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OPC UA Item node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-item-node-W0_AiLIkYO-1008.jpeg" width="1008" height="720" /></picture></p>
</li>
<li>
<p>Connect the output of the <strong>Inject</strong> node to the input of the <strong>Item</strong> node.</p>
</li>
<li>
<p>Add an <strong>OPC UA Client</strong> node and set its <strong>Action</strong> to <code>read</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-client-read-node-_vGCz8aKFX-1006.avif 1006w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-client-read-node-_vGCz8aKFX-1006.webp 1006w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OPC UA Client node configured for reading" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-client-read-node-_vGCz8aKFX-1006.jpeg" width="1006" height="542" /></picture></p>
</li>
<li>
<p>Select the endpoint configuration you created earlier.</p>
</li>
<li>
<p>Connect the output of the <strong>Item</strong> node to the input of the <strong>Client</strong> node, then connect the <strong>Client</strong> node's top output to a <strong>Debug</strong> node.</p>
</li>
</ol>
<blockquote>
<p>The <strong>OPC UA Client</strong> node has three outputs: the top carries the data payload, the middle indicates connection status, and the bottom provides raw responses for debugging.</p>
</blockquote>
<ol start="7">
<li>Deploy the flow and click the <strong>Inject</strong> button to trigger the read.</li>
</ol>
<p>You should see the tag value appear in the debug panel. This confirms that communication is working correctly.</p>
<p>You can also pass the Node ID dynamically using <code>msg.topic</code> from the Inject node if you prefer not to use an Item node.</p>
<div id="nr-flow-189" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow189 = "\n[{\"id\":\"d128582dda7adbed\",\"type\":\"OpcUa-Client\",\"z\":\"ead97ed756a13a15\",\"endpoint\":\"a4df18253e5a79a0\",\"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,\"setstatusandtime\":false,\"keepsessionalive\":false,\"name\":\"\",\"x\":580,\"y\":240,\"wires\":[[\"3fc16bf912f16169\"],[\"17724a6889ec7378\"],[\"c6b0f16ef7371699\"]]},{\"id\":\"3fc16bf912f16169\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Tag Value\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":740,\"y\":200,\"wires\":[]},{\"id\":\"2691196551812a21\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=3;i=1001\",\"datatype\":\"Boolean\",\"value\":\"\",\"name\":\"OPC UA Item Node\",\"x\":370,\"y\":240,\"wires\":[[\"d128582dda7adbed\"]]},{\"id\":\"96d17841a7f13ac4\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Read tag\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":200,\"y\":200,\"wires\":[[\"2691196551812a21\"]]},{\"id\":\"17724a6889ec7378\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Errors\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":730,\"y\":240,\"wires\":[]},{\"id\":\"c6b0f16ef7371699\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Raw Respons\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":280,\"wires\":[]},{\"id\":\"a4df18253e5a79a0\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://192.168.0.10:4840\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow189.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-189') })</script>
<h4 id="reading-multiple-tags" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#reading-multiple-tags"></a> Reading Multiple Tags</h4>
<p>Batch reading improves performance when you need multiple data points from your equipment</p>
<ol>
<li>Drag an <strong>OPC UA Client</strong> node and set its <strong>Action</strong> to "READ MULTIPLE".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/read-multiple-JLsIEP9Ouj-990.avif 990w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/read-multiple-JLsIEP9Ouj-990.webp 990w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing OPC UA Client node with "READ MULTIPLE" action selected" loading="lazy" decoding="async" src="https://flowfuse.com/img/read-multiple-JLsIEP9Ouj-990.jpeg" width="990" height="536" /></picture><br />
<em>Screenshot showing OPC UA Client node with "READ MULTIPLE" action selected</em></p>
<ol start="2">
<li>Select the endpoint configuration.</li>
<li>Add an <strong>OPC UA Item</strong> node for each tag you want to read.</li>
<li>Add an <strong>Inject</strong> node for each Item node to trigger it.</li>
<li>Connect each Inject node to its corresponding Item node.</li>
<li>Wire all Item nodes into the OPC UA Client node.</li>
<li>Add a <strong>Debug</strong> node to the top output of the <strong>Client</strong> node.</li>
<li>Deploy the flow.</li>
<li>Click each Inject node once, the client node will store the tag definitions.</li>
<li>Send a message with <code>msg.topic = "readmultiple"</code> to trigger the actual read.</li>
<li>To clear stored items, send <code>msg.topic = "clearitems"</code>.</li>
</ol>
<p>You now have a flexible setup for reading multiple values from your PLC on demand.</p>
<div id="nr-flow-190" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow190 = "\n[{\"id\":\"6f5e2b1cbce15025\",\"type\":\"OpcUa-Client\",\"z\":\"ead97ed756a13a15\",\"endpoint\":\"\",\"action\":\"readmultiple\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"useTransport\":false,\"maxChunkCount\":\"\",\"maxMessageSize\":\"\",\"receiveBufferSize\":\"\",\"sendBufferSize\":\"\",\"setstatusandtime\":false,\"keepsessionalive\":false,\"name\":\"\",\"x\":580,\"y\":500,\"wires\":[[\"28b575f06bbbe7a7\"],[\"139d346aab204f2f\"],[\"3497d5566fb78f5f\"]]},{\"id\":\"4daa958d34c648c5\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=5;s=Counter1\",\"datatype\":\"Int32\",\"value\":\"\",\"name\":\"\",\"x\":380,\"y\":480,\"wires\":[[\"6f5e2b1cbce15025\"]]},{\"id\":\"baa2733ca1fcb69d\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Add item\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":200,\"y\":480,\"wires\":[[\"4daa958d34c648c5\"]]},{\"id\":\"dfd96a4dfe6330af\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=5;s=Random1\",\"datatype\":\"Double\",\"value\":\"\",\"name\":\"\",\"x\":380,\"y\":520,\"wires\":[[\"6f5e2b1cbce15025\"]]},{\"id\":\"b8d5e40a98b1fb9f\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Add item\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":200,\"y\":520,\"wires\":[[\"dfd96a4dfe6330af\"]]},{\"id\":\"3f63c46f499c3bca\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Read multiple items\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"readmultiple\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":370,\"y\":440,\"wires\":[[\"6f5e2b1cbce15025\"]]},{\"id\":\"75c7927996cf44c2\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Clear nodeId array\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"clearitems\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":370,\"y\":560,\"wires\":[[\"6f5e2b1cbce15025\"]]},{\"id\":\"28b575f06bbbe7a7\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Tag Value\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":460,\"wires\":[]},{\"id\":\"139d346aab204f2f\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Errors\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":750,\"y\":500,\"wires\":[]},{\"id\":\"3497d5566fb78f5f\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Raw Response\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":780,\"y\":540,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow190.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-190') })</script>
<h3 id="writing-values" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#writing-values"></a> Writing Values</h3>
<p>In addition to reading data, OPC UA also allows you to write control signals or parameters to your equipment.</p>
<h4 id="writing-a-single-tag" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#writing-a-single-tag"></a> Writing a Single Tag</h4>
<p>To write a single value:</p>
<ol>
<li>
<p>Drag an <strong>Inject</strong> node onto the canvas (used to trigger the write operation).</p>
</li>
<li>
<p>Add an <strong>OPC UA Item</strong> node and configure:</p>
<ul>
<li><strong>Node ID</strong>: Enter the target identifier.</li>
<li><strong>Data Type</strong>: Choose the appropriate type (e.g., <code>Boolean</code>, <code>Double</code>).</li>
<li><strong>Value</strong>: Enter the value to write.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-item-node-write-1zi0ynsZpx-1016.avif 1016w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-item-node-write-1zi0ynsZpx-1016.webp 1016w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing OPC UA Item node configuration for write operation" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-item-node-write-1zi0ynsZpx-1016.jpeg" width="1016" height="684" /></picture><br />
<em>OPC UA Item node configured for a write operation</em></p>
</li>
<li>
<p>Connect the <strong>Inject</strong> node to the <strong>Item</strong> node.</p>
</li>
<li>
<p>Add an <strong>OPC UA Client</strong> node and set its <strong>Action</strong> to <code>WRITE</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-client-write-ops-B1xwiYE1lV-998.avif 998w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-client-write-ops-B1xwiYE1lV-998.webp 998w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing OPC UA Client node with "WRITE" action selected" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-client-write-ops-B1xwiYE1lV-998.jpeg" width="998" height="530" /></picture><br />
<em>OPC UA Client node with "WRITE" action selected</em></p>
</li>
<li>
<p>Select the endpoint configuration you created earlier.</p>
</li>
<li>
<p>Connect the <strong>Item</strong> node to the <strong>Client</strong> node, then connect the <strong>Client</strong> node's top output to a <strong>Debug</strong> node.</p>
</li>
<li>
<p>Deploy the flow and click the <strong>Inject</strong> button to trigger the write.</p>
</li>
</ol>
<p>The OPC UA Client node will confirm the operation with a status like <strong>"values written"</strong>.</p>
<div id="nr-flow-191" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow191 = "\n[{\"id\":\"c922a70d48ecba6f\",\"type\":\"OpcUa-Client\",\"z\":\"ead97ed756a13a15\",\"endpoint\":\"\",\"action\":\"write\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"useTransport\":false,\"maxChunkCount\":\"\",\"maxMessageSize\":\"\",\"receiveBufferSize\":\"\",\"sendBufferSize\":\"\",\"setstatusandtime\":false,\"keepsessionalive\":false,\"name\":\"\",\"x\":520,\"y\":480,\"wires\":[[\"7eb010a4671ac181\"],[\"392bb1fd60c29baf\"],[\"e9d279677dbc87e8\"]]},{\"id\":\"5ff1e3c2d5977a34\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=5;s=Counter1\",\"datatype\":\"Int32\",\"value\":\"20\",\"name\":\"\",\"x\":340,\"y\":480,\"wires\":[[\"c922a70d48ecba6f\"]]},{\"id\":\"8cdd141dbdb350c7\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Write\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":190,\"y\":480,\"wires\":[[\"5ff1e3c2d5977a34\"]]},{\"id\":\"7eb010a4671ac181\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Tag Value\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":680,\"y\":440,\"wires\":[]},{\"id\":\"392bb1fd60c29baf\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Errors\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":670,\"y\":480,\"wires\":[]},{\"id\":\"e9d279677dbc87e8\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Raw Response\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":700,\"y\":520,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow191.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-191') })</script>
<h4 id="writing-multiple-tags" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#writing-multiple-tags"></a> Writing Multiple Tags</h4>
<p>To write multiple values at once, follow this pattern:</p>
<ol>
<li>
<p>Add an <strong>OPC UA Client</strong> node and set its <strong>Action</strong> to <code>WRITE MULTIPLE</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-client-write-multiple-iQr2aK9dPg-1002.avif 1002w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-client-write-multiple-iQr2aK9dPg-1002.webp 1002w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing OPC UA Client node with "WRITE MULTIPLE" action selected" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-client-write-multiple-iQr2aK9dPg-1002.jpeg" width="1002" height="528" /></picture><br />
<em>OPC UA Client node configured for writing multiple values</em></p>
</li>
<li>
<p>Select the appropriate endpoint configuration.</p>
</li>
<li>
<p>Add multiple <strong>OPC UA Item</strong> nodes, each configured with a <strong>Node ID</strong>, <strong>Data Type</strong>, and <strong>Value</strong> to be written.</p>
</li>
<li>
<p>Add an <strong>Inject</strong> node for each <strong>Item</strong> node.</p>
</li>
<li>
<p>Connect each <strong>Inject</strong> node to its corresponding <strong>Item</strong> node, then connect all <strong>Item</strong> nodes to the <strong>Client</strong> node.</p>
</li>
<li>
<p>Add a <strong>Debug</strong> node to the top output of the <strong>Client</strong> node.</p>
</li>
<li>
<p>Deploy the flow and trigger all <strong>Inject</strong> nodes to load the values.</p>
</li>
<li>
<p>To execute the write operation, send a message with <code>msg.topic = "writemultiple"</code>.</p>
</li>
<li>
<p>To clear the stored items, send a message with <code>msg.topic = "clearitems"</code>.</p>
</li>
</ol>
<p>This setup allows you to prepare multiple tag values and write them all at once, giving you precise control through a single command.</p>
<div id="nr-flow-192" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow192 = "\n[{\"id\":\"ed421a9.d6319e8\",\"type\":\"OpcUa-Client\",\"z\":\"ead97ed756a13a15\",\"endpoint\":\"\",\"action\":\"writemultiple\",\"deadbandtype\":\"a\",\"deadbandvalue\":1,\"time\":10,\"timeUnit\":\"s\",\"certificate\":\"n\",\"localfile\":\"\",\"localkeyfile\":\"\",\"useTransport\":false,\"maxChunkCount\":\"\",\"maxMessageSize\":\"\",\"receiveBufferSize\":\"\",\"sendBufferSize\":\"\",\"setstatusandtime\":false,\"keepsessionalive\":false,\"name\":\"\",\"x\":560,\"y\":420,\"wires\":[[\"b0788fb9285c48ea\"],[\"bde690208cbf2c4c\"],[\"0883826b4a0ca030\"]]},{\"id\":\"96bd763.14a9308\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=3;i=1007\",\"datatype\":\"Double\",\"value\":\"1.0\",\"name\":\"\",\"x\":360,\"y\":400,\"wires\":[[\"ed421a9.d6319e8\"]]},{\"id\":\"d8a68c7a.a73008\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Add item\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":180,\"y\":400,\"wires\":[[\"96bd763.14a9308\"]]},{\"id\":\"8ae51c8c.20bd3\",\"type\":\"OpcUa-Item\",\"z\":\"ead97ed756a13a15\",\"item\":\"ns=3;i=1008\",\"datatype\":\"Int32\",\"value\":\"50\",\"name\":\"\",\"x\":360,\"y\":440,\"wires\":[[\"ed421a9.d6319e8\"]]},{\"id\":\"1335adce.7f46ba\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Add item\",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":180,\"y\":440,\"wires\":[[\"8ae51c8c.20bd3\"]]},{\"id\":\"2c050a3d.91f496\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Write multiple items\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"writemultiple\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":350,\"y\":360,\"wires\":[[\"ed421a9.d6319e8\"]]},{\"id\":\"690e4f9f.faeca\",\"type\":\"inject\",\"z\":\"ead97ed756a13a15\",\"name\":\"Clear nodeId array\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"clearitems\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":350,\"y\":480,\"wires\":[[\"ed421a9.d6319e8\"]]},{\"id\":\"b0788fb9285c48ea\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Tag Value\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":740,\"y\":380,\"wires\":[]},{\"id\":\"bde690208cbf2c4c\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Errors\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":730,\"y\":420,\"wires\":[]},{\"id\":\"0883826b4a0ca030\",\"type\":\"debug\",\"z\":\"ead97ed756a13a15\",\"name\":\"Raw Response\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":760,\"y\":460,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow192.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-192') })</script>
<h2 id="what%E2%80%99s-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/reading-and-writing-plc-data-using-opc-ua/#what%E2%80%99s-next"></a> What’s Next</h2>
<p>You’ve now mastered the fundamentals of OPC UA integration—connecting to servers, browsing tags, and reading or writing data. These core building blocks lay the foundation for powerful industrial automation.</p>
<p>In real deployments, you will want more than Inject nodes and debug panels. With <strong>FlowFuse Dashboard 2.0</strong>, you can build full operator interfaces—live gauges, control buttons, trend charts—fully connected to your OPC UA data.</p>
<p>This guide covered the basics, but OPC UA offers far more. In the next article, we will explore:</p>
<ul>
<li>Subscriptions for real-time monitoring without polling</li>
<li>Events and alarms directly from equipment</li>
<li>Historical data queries for trend analysis</li>
<li>Method calls to execute functions on your devices</li>
</ul>
<p>When it is time to move beyond prototypes, <strong>FlowFuse</strong> delivers what industrial systems truly need—remote device management, instant rollbacks with full version control, built-in team collaboration, and high availability you can trust.</p>
<p>If you’re ready to simplify your OPC UA integration and scale industrial workflows with Node-RED, <a href="https://app.flowfuse.com/account/create">start your free trial</a> of FlowFuse today.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/Connecting Manufacturing Equipment via Serial PortLearn how to bring serial-connected equipment online using Node-RED and FlowFuse2025-07-09T00:00:00ZSumit Shinde<p>Many factories rely on machines, both new and old, that communicate via traditional serial interfaces such as <strong>RS-232, RS-422, or RS-485</strong>. These machines remain reliable but can be challenging to integrate with modern systems due to their connectivity style.</p>
<!--more-->
<p>This blog shows how to use FlowFuse to connect manufacturing equipment to collect data and enable monitoring without modifying the original hardware.</p>
<h2 id="making-sense-of-serial-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#making-sense-of-serial-communication"></a> Making Sense of Serial Communication</h2>
<p>Before diving into the wiring and flow configuration, it helps to understand how serial communication works—and why it is still relevant in industrial settings.</p>
<p>Serial ports move data one bit at a time, like passing beads on a string. This may sound old-fashioned, but it remains one of the most reliable and predictable ways to connect machines.</p>
<h3 id="data-direction%3A-which-way-does-it-flow%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#data-direction%3A-which-way-does-it-flow%3F"></a> Data Direction: Which Way Does It Flow?</h3>
<p>Not all serial connections behave the same. The direction of data flow is defined by its duplex mode:</p>
<ul>
<li><strong>Simplex</strong>: One-way only. Like a speaker giving a lecture—the machine talks, you just listen. This is common for devices like weight scales or scanners that only output data.</li>
<li><strong>Half-Duplex</strong>: Data can flow in both directions, but only one side can transmit at a time. This is like using a walkie-talkie. It is the most common operational mode for <strong>RS-485</strong> (using two wires), where multiple devices share the same communication line.</li>
<li><strong>Full-Duplex</strong>: Two-way, simultaneous communication. This is like a phone call—both sides can talk and listen at once. This is the typical mode for <strong>RS-232</strong> (using separate transmit and receive lines) and <strong>RS-422</strong>, which is designed for full-duplex, multi-drop scenarios (one sender, multiple listeners).</li>
</ul>
<p>Across industrial environments, serial interfaces like <strong>RS-232, RS-422, and RS-485</strong> remain prevalent for device communication, depending on the wiring and number of connected devices.</p>
<h3 id="data-format%3A-how-is-it-structured%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#data-format%3A-how-is-it-structured%3F"></a> Data Format: How Is It Structured?</h3>
<p>Machines are picky—they expect data to arrive in a specific format. Here are the key pieces that make up a serial data <strong>frame</strong>:</p>
<table>
<thead>
<tr>
<th style="text-align:left">Setting</th>
<th style="text-align:left">What It Means</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left"><strong>Baud Rate</strong></td>
<td style="text-align:left">Speed of transmission (e.g., 9600 or 115200)</td>
</tr>
<tr>
<td style="text-align:left"><strong>Data Bits</strong></td>
<td style="text-align:left">The actual data, usually 7 or 8 bits</td>
</tr>
<tr>
<td style="text-align:left"><strong>Parity</strong></td>
<td style="text-align:left">Optional error check—even, odd, or none</td>
</tr>
<tr>
<td style="text-align:left"><strong>Stop Bits</strong></td>
<td style="text-align:left">Marks the end of each message</td>
</tr>
</tbody>
</table>
<h3 id="interface-types%3A-how-devices-physically-connect" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#interface-types%3A-how-devices-physically-connect"></a> Interface Types: How Devices Physically Connect</h3>
<p>Different machines use different physical standards. The most common are:</p>
<ul>
<li><strong>RS-232</strong>: Typically full-duplex and one-to-one. Good for short-distance device communication.</li>
<li><strong>RS-422</strong>: Full-duplex, multi-drop (one sender, multiple receivers). Used for longer distances than RS-232.</li>
<li><strong>RS-485</strong>: Typically half-duplex and multi-device. Ideal for networks and even longer cable runs.</li>
<li><strong>USB (via adapter)</strong>: Most modern PCs and gateways use USB-to-Serial adapters to talk to RS-232/422/485 devices.</li>
</ul>
<h2 id="setting-up-serial-communication-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#setting-up-serial-communication-with-flowfuse"></a> Setting Up Serial Communication with FlowFuse</h2>
<p>Now that you understand how serial communication works and what kind of interfaces your machine might use, the next step is to put that knowledge into practice.</p>
<p>Using <strong>FlowFuse</strong>, you can easily establish serial communication, process the data, and integrate it into dashboards or automated workflows, all without modifying the original hardware.</p>
<p>Let's walk through how to set this up.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#prerequisites"></a> Prerequisites</h3>
<p>Before we start, ensure the following prerequisites are met:</p>
<ul>
<li><strong>Hardware Connection:</strong> The machine must be physically connected to your system using a serial interface.</li>
<li><strong>Node-RED Instance:</strong> Make sure you have an instance of Node-RED up and running. The quickest way to do this is via FlowFuse. If you don't have an account, check out our free trial.</li>
<li><strong>Serialport Node:</strong> Install the <a href="https://flows.nodered.org/node/node-red-node-serialport">node-red-node-serialport</a> package if it is not already available in your palette.</li>
</ul>
<h3 id="configuring-the-serial-port-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#configuring-the-serial-port-node"></a> Configuring the Serial Port Node</h3>
<p>After installing the <code>node-red-node-serialport</code> package, follow these steps to configure serial communication in your Node-RED flow:</p>
<ol>
<li>
<p>Drag a <strong>Serial In</strong> node from the Node-RED palette onto the canvas.</p>
</li>
<li>
<p>Double-click the node to open the configuration dialog.</p>
</li>
<li>
<p>Click the pencil icon next to the <strong>Serial Port</strong> field to add a new port configuration.</p>
</li>
<li>
<p>Enter the serial port path (e.g., <code>/dev/ttyUSB0</code> on Linux or <code>COM3</code> on Windows). You can also click the <strong>search</strong> option to list available ports.</p>
</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/searching-path-QgOCfC4CHV-1036.gif 1036w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of Node-RED serial port node configuration showing available serial ports after clicking the search option." loading="lazy" decoding="async" src="https://flowfuse.com/img/searching-path-QgOCfC4CHV-1036.webp" width="1036" height="784" /></picture>
<em>Screenshot of Node-RED serial port node configuration showing available serial ports after clicking the search option.</em></p>
<ol start="6">
<li>
<p>Set the <strong>baud rate</strong>, <strong>data bits</strong>, <strong>stop bits</strong>, and <strong>parity</strong> according to your machine’s specifications. These values must match the device exactly, or communication will fail or result in corrupted data.</p>
</li>
<li>
<p>Optionally, define an <strong>input delimiter</strong>, such as <code>\n</code> or <code>\r</code>, to segment incoming messages if your device sends data in lines or chunks.
If the output is fixed-length, you can configure it to wait for a specific number of characters. You can also set a <strong>timeout</strong> to receive data at regular intervals.
Later in the output section, you can choose to <strong>add characters</strong> back to the message, such as restoring the line break.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/input-output-serial-node-0FeWoZTLSP-1000.avif 1000w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/input-output-serial-node-0FeWoZTLSP-1000.webp 1000w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of input and output settings in the Node-RED serial port node, showing options like delimiter, character count, and timeout." loading="lazy" decoding="async" src="https://flowfuse.com/img/input-output-serial-node-0FeWoZTLSP-1000.jpeg" width="1000" height="422" /></picture>
<em>Screenshot of input and output settings in the Node-RED serial port node, showing options like delimiter, character count, and timeout.</em></p>
</li>
<li>
<p>Click <strong>Done</strong> to save the configuration.</p>
</li>
</ol>
<p>Once the serial port is correctly configured and the device is connected, the <strong>serial in</strong> node will show a "connected" status below the node with a small green square.</p>
<p data-zoomable=""><picture><source type="image/avif" srcset="https://flowfuse.com/img/serial-port-node-status-1TZSARBARk-352.avif 352w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/serial-port-node-status-1TZSARBARk-352.webp 352w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Serial In node in Node-RED showing a green square that indicates a successful connection to the serial port." loading="lazy" decoding="async" src="https://flowfuse.com/img/serial-port-node-status-1TZSARBARk-352.jpeg" width="352" height="158" /></picture>
<em>Screenshot of the Serial In node in Node-RED showing a green square that indicates a successful connection to the serial port.</em></p>
<h3 id="writing-to-serial-port" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#writing-to-serial-port"></a> Writing to Serial Port</h3>
<p>To send data to a machine, use the <strong>serial out</strong> node in Node-RED. This is often necessary to trigger actions such as starting a process, requesting a reading, or changing an internal state.</p>
<p>Follow these steps to send a command:</p>
<ol>
<li>Drag an <strong>inject</strong> node onto the canvas.</li>
<li>Set the <strong>payload type</strong> appropriate to your machine’s requirements. This could be a string, number, raw buffer, or JSON object.</li>
<li>Add a <strong>serial out</strong> node and select the configured serial port.</li>
<li>Connect the <strong>inject</strong> node to the <strong>serial out</strong> node.</li>
</ol>
<p>Once deployed, clicking the inject button will send the specified data to the machine via the serial interface.</p>
<p>In this guide, we are using a real machine connected via a serial interface. The machine is programmed to simulate a production process when it receives the <code>"START"</code> command (sent as a string). Once triggered, it begins incrementing the count of good and defect products and sends this data back over the same serial connection.</p>
<p>The next section demonstrates how to read and process this simulated production data using the <strong>serial in</strong> node.</p>
<h3 id="reading-and-processing-serial-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#reading-and-processing-serial-data"></a> Reading and Processing Serial Data</h3>
<p>Follow these steps to read and handle the serial data:</p>
<ol>
<li>Drag a <strong>serial in</strong> node onto the canvas and configure it to use the same serial port.</li>
<li>Add a <strong>debug</strong> node and connect it to the output of the <strong>serial in</strong> node. This helps you inspect the raw payload and confirm that data is being received correctly.</li>
<li>Once you confirm the format of the incoming data, use any of the <strong>change</strong>, <strong>JSON</strong>, or <strong>function</strong> node to parse and convert it into a structured format, here we have used function.</li>
</ol>
<p>In our case, the machine sends production data every 2 seconds in the following format:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/debug-panel-output-6mpPImuTM--580.avif 580w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/debug-panel-output-6mpPImuTM--580.webp 580w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of Node-RED debug panel showing production data sent from the machine every 2 seconds" loading="lazy" decoding="async" src="https://flowfuse.com/img/debug-panel-output-6mpPImuTM--580.jpeg" width="580" height="578" /></picture>
<em>Screenshot of Node-RED debug panel showing production data sent from the machine every 2 seconds.</em></p>
<p>To convert this string into a structured JSON object, you can use a <strong>function</strong> node with the following code:</p>
<div style="position: relative" id="code-container-286">
<pre class="language-javascript"><code id="code-286" class="language-javascript"><span class="token keyword">let</span> parts <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">' '</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />parts<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">part</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> <span class="token punctuation">[</span>key<span class="token punctuation">,</span> value<span class="token punctuation">]</span> <span class="token operator">=</span> part<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">':'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> result<span class="token punctuation">[</span>key<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> result<span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-286" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This transforms the string into a JSON object like:</p>
<div style="position: relative" id="code-container-290">
<pre class="language-json"><code id="code-290" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"GOOD"</span><span class="token operator">:</span> <span class="token number">214</span><span class="token punctuation">,</span><br /> <span class="token property">"DEFECT"</span><span class="token operator">:</span> <span class="token number">22</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-290" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<blockquote>
<p><strong>Tip</strong>: You do not need to know JavaScript to use the <strong>function</strong> node.
If you are using FlowFuse, the built-in <a href="https://www.google.com/search?q=/docs/user/assistant/">FlowFuse Assistant</a> can help you write function code using natural language. Simply provide a sample of the data received from your machine and describe the output you expect — the Assistant will generate the function for you.</p>
</blockquote>
<h3 id="handling-request-response-serial-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#handling-request-response-serial-communication"></a> Handling Request-Response Serial Communication</h3>
<p>Not all machines stream data continuously. Some expect a request command, and only then respond with data. In these cases, using a combination of <strong>inject</strong>, <strong>serial out</strong>, and <strong>serial in</strong> nodes can become tricky—especially if you need to match each request with exactly one response. That’s where the <strong>serial request</strong> node becomes useful.</p>
<p>The <strong>serial request</strong> node handles this entire pattern for you. Internally, it combines the logic of sending a message and waiting for a single reply, working in a <strong>first-in, first-out</strong> manner. This means it will only send the next request after receiving a response (or timeout) for the previous one, making it ideal for synchronous devices.</p>
<p>To use it:</p>
<ol>
<li>Drag a <strong>serial request</strong> node from the palette.</li>
<li>Double-click to configure the port—use the same path and settings as your other serial nodes.</li>
<li>Connect it to an <strong>inject</strong> node configured with the command your machine expects, such as <code>"READ"</code> or <code>"STATUS"</code>.</li>
<li>On the output side, connect a <strong>debug</strong> or <strong>function</strong> node to handle the response.</li>
</ol>
<p>Each time you trigger the inject, the command will be sent over the serial port, and the response will be delivered to the output—ready to be parsed just like before. This approach is clean, predictable, and removes the guesswork from matching writes with reads.</p>
<p>The output message includes <code>msg.payload</code> containing the response (if any), <code>msg.status</code> with the result status, and <code>msg.port</code> for reference.</p>
<p>This node is especially useful for polling machines that respond with production counts, part IDs, temperature readings, or system status—but only when asked.</p>
<h3 id="dynamically-managing-serial-ports" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#dynamically-managing-serial-ports"></a> Dynamically Managing Serial Ports</h3>
<p>In a perfect setup, the serial device is connected, the port is stable, and everything just works. But in practice, hardware is not always so predictable.</p>
<p>For example, You might disconnect and reconnect a USB-to-serial adapter—and now the device shows up as <code>/dev/ttyUSB1</code> instead of <code>/dev/ttyUSB0</code>. Or maybe you need to temporarily release the serial port to flash new firmware onto an Arduino. In some environments, the port assignment could change on every reboot, making it difficult to hardcode anything.</p>
<p>Rather than redeploying or editing your flow every time, Node-RED gives you a more flexible option: the <strong>serial control</strong> node.</p>
<p>This node lets your flow adjust serial communication settings on the fly. You can:</p>
<ul>
<li>Stop the serial connection when needed.</li>
<li>Start it again later.</li>
<li>Even switch to a different port entirely—without touching the Node-RED editor.</li>
</ul>
<p>All of this happens by sending a simple message to the control node.</p>
<p>To stop communication:</p>
<div style="position: relative" id="code-container-377">
<pre class="language-json"><code id="code-377" class="language-json"><span class="token punctuation">{</span> <span class="token property">"enabled"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-377" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>To start it again:</p>
<div style="position: relative" id="code-container-381">
<pre class="language-json"><code id="code-381" class="language-json"><span class="token punctuation">{</span> <span class="token property">"enabled"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-381" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>And if the port changes or needs reconfiguration, you can send everything in one message:</p>
<div style="position: relative" id="code-container-385">
<pre class="language-json"><code id="code-385" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"serialport"</span><span class="token operator">:</span> <span class="token string">"/dev/ttyUSB1"</span><span class="token punctuation">,</span><br /> <span class="token property">"serialbaud"</span><span class="token operator">:</span> <span class="token number">9600</span><span class="token punctuation">,</span><br /> <span class="token property">"databits"</span><span class="token operator">:</span> <span class="token number">8</span><span class="token punctuation">,</span><br /> <span class="token property">"parity"</span><span class="token operator">:</span> <span class="token string">"none"</span><span class="token punctuation">,</span><br /> <span class="token property">"stopbits"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token property">"enabled"</span><span class="token operator">:</span> <span class="token boolean">true</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-385" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This is especially useful when your flow needs to recover automatically—for example, after a USB reconnection—or if you want to let a user select the correct port from a dashboard interface.</p>
<p>Each time a message is received, the node also outputs the current port configuration. This allows you to log or verify changes as part of your flow—making it easy to track what the system is doing behind the scenes.</p>
<p>In short, the <strong>serial control</strong> node adds a layer of resilience and flexibility that is often essential in real-world deployments—where devices come and go, ports are never quite consistent, and downtime is not an option.</p>
<h2 id="key-takeaways" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/connect-legacy-equipment-serial-flowfuse/#key-takeaways"></a> Key Takeaways</h2>
<p>Manufacturing floors often bring together a mix of old and new machines. Among them, many still operate on traditional communication methods that are reliable but difficult to integrate with modern systems. These machines continue to perform their core functions well, but without connectivity, they remain isolated from digital workflows.</p>
<p>With FlowFuse and Node-RED, you can bridge that gap—bringing equipment online without changing or replacing existing hardware. From data collection to triggering actions and monitoring performance, your machines can become part of a connected and intelligent system.</p>
<p>Whether you’re in textiles, precision engineering, or automotive manufacturing, FlowFuse helps you unlock the full potential of your existing equipment. No rip-and-replace needed—just smarter connections. <a href="https://flowfuse.com/contact-us/">Get in touch with us</a> and start building your serial integration flow today.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/How we Built a Smart Manufacturing Order Execution Panel with FlowFuseControl and track manufacturing orders with FlowFuse2025-07-03T00:00:00ZSumit Shinde<p>A few days ago, I had a conversation with a solution architect about how the lack of integration between the shop floor and business systems often leads to missed opportunities and financial losses. He also mentioned that while many manufacturers want to bridge this gap, they often hesitate — mostly due to concerns about complexity of integration or fear of disrupting existing operations.</p>
<!--more-->
<p>But there is no need to be afraid. In this blog, I will walk you through a simple demo that shows how easy it can be to connect production systems with your ERP. With just a small integration, you can improve visibility, avoid manual errors, and prevent unnecessary losses.</p>
<p>To demonstrate this, I built a smart Manufacturing Order (MO) execution panel using FlowFuse, connected to ERP.</p>
<p>Let’s take a closer look.</p>
<h2 id="demo%3A-smart-manufacturing-order-execution-panel-in-action" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#demo%3A-smart-manufacturing-order-execution-panel-in-action"></a> Demo: Smart Manufacturing Order Execution Panel in Action</h2>
<p><lite-youtube videoid="M_CIoHiSW6s" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<p>In this demonstration, an operator starts by selecting a Manufacturing Order (MO) from a list pulled directly from Odoo ERP. The system immediately checks if enough raw materials are available for that specific order.</p>
<p>If the materials are ready, production begins, and the MO status in Odoo is automatically updated to In Progress. The system then tracks the production count in real time. However, if materials are missing, production won’t start, and the operator is notified instantly. Once the produced quantity matches the target, the system automatically stops the line and marks the MO as Done in Odoo, completing the cycle without manual intervention.</p>
<h2 id="how-it-works%3A-behind-the-scenes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#how-it-works%3A-behind-the-scenes"></a> How It Works: Behind the Scenes</h2>
<p>To build this system, I used <strong>FlowFuse</strong> to create a Node-RED flow that connects to <strong>Odoo ERP</strong> and controls a simulated production line.</p>
<h3 id="system-components" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#system-components"></a> System Components</h3>
<ul>
<li>
<p><strong>Odoo ERP</strong><br />
Holds Manufacturing Orders, product details, and inventory data. This integration allows for a two-way conversation between the shop floor and the business's core planning system. For a detailed guide on how to read from and write to Odoo, you can read our article, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/">Connect Your Shop Floor to Your ERP – Odoo Edition</a></p>
</li>
<li>
<p><strong>FlowFuse</strong><br />
Executes logic such as fetching manufacturing orders, checking material availability, updating statuses, controlling production, and building the operator interface.</p>
</li>
<li>
<p><strong>Simulated Production Line (Factory I/O)</strong><br />
Acts as the shop floor. Starts and stops production based on FlowFuse commands.</p>
</li>
<li>
<p><strong>PLC</strong><br />
Receives commands from FlowFuse and controls the actual machinery or simulated production environment.</p>
</li>
<li>
<p><strong>S7 Protocol (S7Comm)</strong><br />
This is the protocol used to communicate with Siemens S7 series PLCs. We use Node-RED nodes within FlowFuse to send control commands (e.g., start/stop production) and read critical data (e.g., produced quantity, machine status) directly from the PLC’s memory blocks. To learn exactly how to set this up, check out our step-by-step tutorial, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/">Getting Started: Integrating Siemens S7 PLCs with Node-RED</a></p>
</li>
</ul>
<p>Below is the full Node-RED flow that powers this smart manufacturing execution panel.</p>
<div id="nr-flow-185" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow185 = "\n[{\"id\":\"FFF0000000000001\",\"type\":\"tab\",\"label\":\"Manufacturing Order Execution Panel\",\"disabled\":false,\"info\":\"\"},{\"id\":\"9aad63553d0d5812\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"4e8b22877e33b496\",\"a0e5e90344e88702\",\"16744562f37152fd\",\"f44680e87cdce3ac\",\"b04986985da9af6d\"],\"x\":14,\"y\":99,\"w\":1272,\"h\":82},{\"id\":\"283e4560434b3d9d\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"13462b8b0f1607f7\",\"8a482fd274547e0f\",\"3efb1c1f34d51979\",\"41b87ca07226c044\",\"dda4ef8740ffb258\",\"226fe05ccb3626e4\",\"51a994a919a9ef23\",\"4556a1575a3c8cf0\",\"e23884a220043451\",\"9df108ae0c62ee39\"],\"x\":14,\"y\":179,\"w\":1272,\"h\":162},{\"id\":\"8df3544692ccd8d8\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"0ce2a3c49003ca9f\",\"b11dfaf019777940\"],\"x\":14,\"y\":359,\"w\":572,\"h\":82},{\"id\":\"0a5f467b0d3f15a9\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"e079fb2baac0a769\",\"e8069c4a2b3f5457\",\"1f9227a4cad571bd\",\"c81b1fb4e1b78f64\",\"befaaf0dd824e0e9\",\"8f4dbf296b38a3c3\"],\"x\":14,\"y\":559,\"w\":912,\"h\":122},{\"id\":\"b9d1401e33f657a5\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"67598322bfa17031\",\"2c3da53c9785237d\"],\"x\":14,\"y\":459,\"w\":352,\"h\":82},{\"id\":\"c85a3940e02e9d05\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#1a1c25\",\"primary\":\"#0094ce\",\"bgPage\":\"#000102\",\"groupBg\":\"#1a1c25\",\"groupOutline\":\"#000000\"},\"sizes\":{\"density\":\"default\",\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${URL}\",\"db\":\"${DB_NAME}\",\"username\":\"${EMAIL}\",\"password\":\"${PASSWORD}\"},{\"id\":\"e70720e10a4d3490\",\"type\":\"ui-page\",\"name\":\"Manufacturing Order Execution Panel\\t\",\"ui\":\"127331d7913b7654\",\"path\":\"/page1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"c85a3940e02e9d05\",\"breakpoints\":[{\"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\":\"127331d7913b7654\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"showPathInSidebar\":false,\"headerContent\":\"page\",\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\",\"showReconnectNotification\":true,\"notificationDisplayTime\":1,\"showDisconnectNotification\":true,\"allowInstall\":true},{\"id\":\"9beea4acdde08f57\",\"type\":\"ui-group\",\"name\":\". \",\"page\":\"e70720e10a4d3490\",\"width\":\"3\",\"height\":\"2\",\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"2aabe3e541e4ba61\",\"type\":\"ui-group\",\"name\":\"Running Order Summary\",\"page\":\"e70720e10a4d3490\",\"width\":\"9\",\"height\":1,\"order\":2,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"9dc2b3b0074096ee\",\"type\":\"ui-group\",\"name\":\"Manufacturing Orders\",\"page\":\"e70720e10a4d3490\",\"width\":\"12\",\"height\":1,\"order\":3,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"a75a1ee3b5119c06\",\"type\":\"s7 endpoint\",\"transport\":\"iso-on-tcp\",\"address\":\"192.168.1.10\",\"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\":\"\",\"vartable\":[{\"addr\":\"QD30\",\"name\":\"Lid Counter\"},{\"addr\":\"Q1.3\",\"name\":\"STOP\"},{\"addr\":\"DB4,X192.0\",\"name\":\"START\"}]},{\"id\":\"13462b8b0f1607f7\",\"type\":\"ui-button\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"group\":\"9beea4acdde08f57\",\"name\":\"\",\"label\":\"START\",\"order\":1,\"width\":\"0\",\"height\":\"0\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"green\",\"textColor\":\"white\",\"iconColor\":\"\",\"enableClick\":true,\"enablePointerdown\":false,\"pointerdownPayload\":\"\",\"pointerdownPayloadType\":\"str\",\"enablePointerup\":false,\"pointerupPayload\":\"\",\"pointerupPayloadType\":\"str\",\"x\":90,\"y\":260,\"wires\":[[\"51a994a919a9ef23\"]]},{\"id\":\"4e8b22877e33b496\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"g\":\"9aad63553d0d5812\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"10\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[]\",\"payloadType\":\"json\",\"x\":110,\"y\":140,\"wires\":[[\"a0e5e90344e88702\"]]},{\"id\":\"a0e5e90344e88702\",\"type\":\"odoo-xmlrpc-search-read\",\"z\":\"FFF0000000000001\",\"g\":\"9aad63553d0d5812\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"mrp.production\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":350,\"y\":140,\"wires\":[[\"16744562f37152fd\"]]},{\"id\":\"16744562f37152fd\",\"type\":\"function\",\"z\":\"FFF0000000000001\",\"g\":\"9aad63553d0d5812\",\"name\":\"getActiveOrdersSummary\",\"func\":\"const simplifiedOrders = msg.payload\\n .filter(order => order.state === \\\"confirmed\\\" || order.state === \\\"progress\\\")\\n .map(order => {\\n return {\\n id: order.id,\\n name: order.name,\\n product: order.product_id?.[1] || \\\"Unknown\\\",\\n quantity: order.product_qty,\\n state: order.state,\\n availability: order.components_availability || \\\"Unknown\\\"\\n };\\n });\\n\\nmsg.payload = simplifiedOrders;\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":590,\"y\":140,\"wires\":[[\"f44680e87cdce3ac\"]]},{\"id\":\"f44680e87cdce3ac\",\"type\":\"ui-table\",\"z\":\"FFF0000000000001\",\"g\":\"9aad63553d0d5812\",\"group\":\"9dc2b3b0074096ee\",\"name\":\"\",\"label\":\"\",\"order\":1,\"width\":0,\"height\":0,\"maxrows\":0,\"passthru\":false,\"autocols\":false,\"showSearch\":false,\"deselect\":true,\"selectionType\":\"click\",\"columns\":[{\"title\":\"ID\",\"key\":\"id\",\"keyType\":\"key\",\"type\":\"text\",\"width\":\"\",\"align\":\"start\"},{\"title\":\"Ref No \",\"key\":\"name\",\"keyType\":\"key\",\"type\":\"text\",\"width\":\"\",\"align\":\"start\"},{\"title\":\"Production Item\",\"key\":\"product\",\"keyType\":\"key\",\"type\":\"text\",\"width\":\"\",\"align\":\"start\"},{\"title\":\"Target Quantity\",\"key\":\"quantity\",\"keyType\":\"key\",\"type\":\"text\",\"width\":\"\",\"align\":\"start\"},{\"title\":\"Order Status\",\"key\":\"state\",\"keyType\":\"key\",\"type\":\"text\",\"width\":\"\",\"align\":\"start\"}],\"mobileBreakpoint\":\"sm\",\"mobileBreakpointType\":\"defaults\",\"action\":\"replace\",\"x\":870,\"y\":140,\"wires\":[[\"b04986985da9af6d\"]]},{\"id\":\"8a482fd274547e0f\",\"type\":\"odoo-xmlrpc-update\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"mrp.production\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":1160,\"y\":260,\"wires\":[[]]},{\"id\":\"3efb1c1f34d51979\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[ [payload.id], {\\\"state\\\": \\\"progress\\\"} ]\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":260,\"wires\":[[\"8a482fd274547e0f\"]]},{\"id\":\"41b87ca07226c044\",\"type\":\"switch\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"Does the MO have a material shortage?\",\"property\":\"payload.availability\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"Not Available\",\"vt\":\"str\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":640,\"y\":240,\"wires\":[[\"226fe05ccb3626e4\"],[\"3efb1c1f34d51979\",\"9df108ae0c62ee39\"]]},{\"id\":\"dda4ef8740ffb258\",\"type\":\"ui-notification\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"ui\":\"127331d7913b7654\",\"position\":\"center center\",\"colorDefault\":true,\"color\":\"#000000\",\"displayTime\":\"3\",\"showCountdown\":true,\"outputs\":1,\"allowDismiss\":true,\"dismissText\":\"Close\",\"allowConfirm\":false,\"confirmText\":\"Confirm\",\"raw\":false,\"className\":\"\",\"name\":\"\",\"x\":1150,\"y\":220,\"wires\":[[]]},{\"id\":\"226fe05ccb3626e4\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"Production cannot begin for this order— required materials are not available.\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"orderSelected\",\"pt\":\"flow\",\"to\":\"{}\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":220,\"wires\":[[\"dda4ef8740ffb258\"]]},{\"id\":\"b04986985da9af6d\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"9aad63553d0d5812\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"orderSelected\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1160,\"y\":140,\"wires\":[[]]},{\"id\":\"51a994a919a9ef23\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"orderSelected\",\"tot\":\"flow\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":320,\"y\":260,\"wires\":[[\"41b87ca07226c044\",\"4556a1575a3c8cf0\"]]},{\"id\":\"4556a1575a3c8cf0\",\"type\":\"ui-template\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"group\":\"2aabe3e541e4ba61\",\"page\":\"\",\"ui\":\"\",\"name\":\"Running Order Summary\",\"order\":1,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"<template>\\n <div class=\\\"kpi-row\\\">\\n <div class=\\\"kpi-box\\\">\\n <div class=\\\"kpi-title\\\">Reference</div>\\n <div class=\\\"kpi-value\\\"></div>\\n </div>\\n <div class=\\\"kpi-box\\\">\\n <div class=\\\"kpi-title\\\">Product</div>\\n <div class=\\\"kpi-value\\\"></div>\\n </div>\\n <div class=\\\"kpi-box\\\">\\n <div class=\\\"kpi-title\\\">Target</div>\\n <div class=\\\"kpi-value\\\"></div>\\n </div>\\n <div class=\\\"kpi-box\\\">\\n <div class=\\\"kpi-title\\\">Produced</div>\\n <div class=\\\"kpi-value\\\"></div>\\n </div>\\n </div>\\n</template>\\n\\n<script>\\n export default {\\n name: 'KpiDisplay',\\n\\n props: {\\n id: {\\n type: String,\\n required: true\\n }\\n },\\n\\n data() {\\n return {\\n data: {\\n name: '',\\n product: '',\\n quantity: null,\\n producedQuantity: null\\n }\\n };\\n },\\n\\n mounted() {\\n const eventName = 'msg-input:' + this.id;\\n console.log('Listening to socket:', eventName);\\n\\n this.$socket.on(eventName, (msg) => {\\n console.log('Message received:', msg);\\n\\n // Merge payload with defaults to avoid missing fields\\n this.data = Object.assign({\\n name: '',\\n product: '',\\n quantity: null,\\n producedQuantity: null\\n }, msg.payload);\\n });\\n }\\n};\\n</script>\\n\\n<style scoped>\\n .kpi-row {\\n display: flex;\\n justify-content: space-between;\\n gap: 2rem;\\n margin: 1rem 0;\\n }\\n\\n .kpi-box {\\n text-align: center;\\n flex: 1;\\n }\\n\\n .kpi-title {\\n font-size: 1.3rem;\\n color: white;\\n margin-bottom: 0.5rem;\\n }\\n\\n .kpi-value {\\n font-size: 1.3rem;\\n color: white;\\n }\\n</style>\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":590,\"y\":280,\"wires\":[[]]},{\"id\":\"e23884a220043451\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"endpoint\":\"a75a1ee3b5119c06\",\"variable\":\"Start\",\"name\":\"\",\"x\":1110,\"y\":300,\"wires\":[]},{\"id\":\"9df108ae0c62ee39\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"283e4560434b3d9d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"true\",\"tot\":\"bool\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":300,\"wires\":[[\"e23884a220043451\"]]},{\"id\":\"0ce2a3c49003ca9f\",\"type\":\"s7 in\",\"z\":\"FFF0000000000001\",\"g\":\"8df3544692ccd8d8\",\"endpoint\":\"a75a1ee3b5119c06\",\"mode\":\"single\",\"variable\":\"Lid Counter\",\"diff\":true,\"name\":\"\",\"x\":110,\"y\":400,\"wires\":[[\"b11dfaf019777940\"]]},{\"id\":\"b11dfaf019777940\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"8df3544692ccd8d8\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"orderSelected.producedQuantity\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":400,\"y\":400,\"wires\":[[]]},{\"id\":\"e079fb2baac0a769\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"orderSelected\",\"payloadType\":\"flow\",\"x\":150,\"y\":620,\"wires\":[[\"befaaf0dd824e0e9\"]]},{\"id\":\"91158f2206527fc0\",\"type\":\"ui-template\",\"z\":\"FFF0000000000001\",\"group\":\"\",\"page\":\"e70720e10a4d3490\",\"ui\":\"\",\"name\":\"CSS\",\"order\":0,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\".v-card-title{\\n text-align:center;\\n}\\nth,td{\\n border: 1px solid white;\\n}\\n.nrdb-ui-led-bulb{\\n height:85px !important;\\n margin-left:auto;\\n margin-right:auto;\\n}\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"page:style\",\"className\":\"\",\"x\":90,\"y\":60,\"wires\":[[]]},{\"id\":\"e8069c4a2b3f5457\",\"type\":\"odoo-xmlrpc-update\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"mrp.production\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":800,\"y\":640,\"wires\":[[]]},{\"id\":\"1f9227a4cad571bd\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[ [data.id], {\\\"state\\\": \\\"done\\\"} ]\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":580,\"y\":640,\"wires\":[[\"e8069c4a2b3f5457\"]]},{\"id\":\"67598322bfa17031\",\"type\":\"ui-button\",\"z\":\"FFF0000000000001\",\"g\":\"b9d1401e33f657a5\",\"group\":\"9beea4acdde08f57\",\"name\":\"\",\"label\":\"STOP\",\"order\":2,\"width\":\"0\",\"height\":\"0\",\"emulateClick\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"iconPosition\":\"left\",\"payload\":\"\",\"payloadType\":\"str\",\"topic\":\"topic\",\"topicType\":\"msg\",\"buttonColor\":\"red\",\"textColor\":\"white\",\"iconColor\":\"\",\"enableClick\":true,\"enablePointerdown\":false,\"pointerdownPayload\":\"\",\"pointerdownPayloadType\":\"str\",\"enablePointerup\":false,\"pointerupPayload\":\"\",\"pointerupPayloadType\":\"str\",\"x\":90,\"y\":500,\"wires\":[[\"2c3da53c9785237d\"]]},{\"id\":\"2c3da53c9785237d\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"b9d1401e33f657a5\",\"endpoint\":\"a75a1ee3b5119c06\",\"variable\":\"STOP\",\"name\":\"\",\"x\":290,\"y\":500,\"wires\":[]},{\"id\":\"c81b1fb4e1b78f64\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"true\",\"tot\":\"bool\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":580,\"y\":600,\"wires\":[[\"8f4dbf296b38a3c3\"]]},{\"id\":\"befaaf0dd824e0e9\",\"type\":\"function\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"name\":\"checkIfOrderIsComplete\",\"func\":\"let data = msg.payload;\\nif (Number(data.quantity) === Number(data.producedQuantity)) {\\n msg.payload = true;\\n msg.data = data\\n return msg;\\n}\\nreturn null;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":370,\"y\":620,\"wires\":[[\"1f9227a4cad571bd\",\"c81b1fb4e1b78f64\"]]},{\"id\":\"8f4dbf296b38a3c3\",\"type\":\"s7 out\",\"z\":\"FFF0000000000001\",\"g\":\"0a5f467b0d3f15a9\",\"endpoint\":\"a75a1ee3b5119c06\",\"variable\":\"STOP\",\"name\":\"\",\"x\":750,\"y\":600,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow185.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-185') })</script>
<h3 id="workflow-breakdown" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#workflow-breakdown"></a> Workflow Breakdown</h3>
<pre class="mermaid">sequenceDiagram participant Operator participant FlowFuse participant Odoo participant PLC Operator->>FlowFuse: Select MO FlowFuse->>Odoo: Fetch MO details Odoo-->>FlowFuse: Return MO + Material info alt Materials Available FlowFuse->>Odoo: Update MO to "In Progress" FlowFuse->>PLC: Start production loop While producing PLC-->>FlowFuse: Report produced quantity end FlowFuse->>PLC: Stop production FlowFuse->>Odoo: Update MO to "Done" else Materials Not Available FlowFuse-->>Operator: Notify: Cannot start end </pre>
<ol>
<li>
<p><strong>Fetch Manufacturing Orders</strong><br />
The system pulls a list of <em>confirmed</em> or <em>in progress</em> MOs from Odoo using HTTP requests with help of Odoo node.</p>
</li>
<li>
<p><strong>Check Raw Material Availability</strong><br />
When an operator selects an MO, FlowFuse checks if enough raw materials are available in Odoo.</p>
</li>
<li>
<p><strong>Start Production</strong><br />
If materials are available:</p>
<ul>
<li>The MO status is updated to <em>In Progress</em></li>
<li>The simulated line starts producing</li>
</ul>
</li>
<li>
<p><strong>Track Quantity in Real Time</strong><br />
As the line runs, FlowFuse keeps track of the produced quantity.</p>
</li>
<li>
<p><strong>Stop and Complete the MO</strong><br />
When the produced quantity matches the MO target:</p>
<ul>
<li>FlowFuse sends a stop command to the PLC</li>
<li>The MO status in Odoo is updated to <em>Done</em></li>
</ul>
</li>
</ol>
<h3 id="why-this-matters-for-business" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#why-this-matters-for-business"></a> Why This Matters for Business</h3>
<p>This demo might seem simple, but it solves some of the most common and expensive problems on the shop floor. Think about the daily headaches: an operator starts a big job, only to find out halfway through that a key material is missing, forcing the entire line to stop. Or, they produce 10% more than the order required, creating waste that just sits in inventory.</p>
<p>This smart panel is designed to prevent those exact scenarios. By connecting your production line directly to your business systems, it:</p>
<ul>
<li><strong>Prevents material shortages</strong> by automatically checking for raw materials before a job can even start.</li>
<li><strong>Eliminates overproduction</strong> by stopping the line the moment the target quantity is hit.</li>
<li><strong>Gets rid of manual data entry</strong> by instantly updating the order status in the ERP.</li>
</ul>
<p>This means managers get a live, accurate view of what’s happening on the floor, not data from hours ago. The best part is that you don't need to overhaul your entire operation or buy a huge, complex system to get these benefits. A smart, focused integration like this can deliver real results, quickly.</p>
<h2 id="ready-to-connect-your-shop-floor%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/smart-manufacturing-order-panel-flowfuse/#ready-to-connect-your-shop-floor%3F"></a> Ready to Connect Your Shop Floor?</h2>
<p>This demo shows how even a small, targeted integration between your production line and ERP can eliminate manual errors, reduce waste, and improve visibility — without overhauling your entire system.</p>
<p>If you are exploring how to bring these kinds of improvements to your manufacturing operations, let’s talk.</p>
<p>We’d be happy to discuss how FlowFuse can help you build custom, scalable solutions tailored to your factory’s needs.</p>
<p>👉 <a href="https://flowfuse.com/contact-us/">Get in touch with us</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/FlowFuse 2.19: More Powerful AI in Node-RED, Drop-In Blueprints, Memory Monitoring, and Faster OnboardingAI enhancements to Node-RED, streamlined onboarding with social authentication and device flow selection, improved Blueprint experience, more comprehensive performance monitoring, and a refreshed user interface.2025-07-03T00:00:00ZGreg Stoutenburg<p>This release focuses on speeding time to value with more powerful AI functionality within Node-RED, along with reducing friction for new users while enhancing the experience for existing users through improved Blueprint functionality, comprehensive performance monitoring, and a modernized interface that reflects FlowFuse's evolution.</p>
<!--more-->
<h2 id="ai-enhancements-to-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#ai-enhancements-to-node-red"></a> AI Enhancements to Node-RED</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/assistant-0-3-0-flow-explainer-3tbNoRlb4T-1089-x28AF79H5m-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="GIF of AI Flow Explainer" loading="lazy" decoding="async" src="https://flowfuse.com/img/assistant-0-3-0-flow-explainer-3tbNoRlb4T-1089-x28AF79H5m-650.webp" width="650" height="471" /></picture>
<em>GIF of AI Flow Explainer</em></p>
<p>The FlowFuse Assistant can now do more than create a Function node based on your text instructions. With this release, you can highlight a flow in the Node-RED editor and ask the FlowFuse Assistant to explain the purpose of the flow. This new functionality is perfect for learning and collaboration.</p>
<p>You can also use the FlowFuse assistant to create Dashboard templates with HTML, VUE, Vuetify, and CSS, allowing you to build even faster.</p>
<p>Stay tuned for more developments as we continue to add enhancements to the Node-RED editor in FlowFuse to enable even faster development.</p>
<h2 id="comprehensive-performance-monitoring" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#comprehensive-performance-monitoring"></a> Comprehensive Performance Monitoring</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Memory-aMUMOJk-xJ-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Memory-aMUMOJk-xJ-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Memory in Performance Feature" loading="lazy" decoding="async" src="https://flowfuse.com/img/Memory-aMUMOJk-xJ-650.jpeg" width="650" height="251" /></picture>
<em>Screenshot of Memory Monitoring</em>
Building on our existing CPU monitoring capabilities, we've added memory usage tracking to the Performance feature. This enhancement provides:</p>
<ul>
<li>Complete visibility into both CPU and memory utilization</li>
<li>Better understanding of instance resource consumption</li>
<li>More informed decision-making for scaling and optimization</li>
</ul>
<p>With these features, you can now get a holistic view of your instance performance and make informed decisions about resource allocation.</p>
<h2 id="add-blueprints-to-existing-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#add-blueprints-to-existing-instances"></a> Add Blueprints to Existing Instances</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/blueprint-import-IgwBMRtt12-596.avif 596w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/blueprint-import-IgwBMRtt12-596.webp 596w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Blueprint Import" loading="lazy" decoding="async" src="https://flowfuse.com/img/blueprint-import-IgwBMRtt12-596.jpeg" width="596" height="506" /></picture>
<em>Screenshot of Blueprint Import</em></p>
<p>Previously, Blueprints could only be used when creating new instances. Now you can import Blueprints into existing instances, making them much more versatile. Blueprints now appear as an option in the Import menu.</p>
<p>And if you're running Node-RED version 4.1, when you select a Blueprint to import that uses nodes not in your current palette, it will offer to install them automatically.</p>
<p>This change transforms Blueprints from a one-time instance creation tool into a constantly useful resource to speed your development with Node-RED.</p>
<h2 id="social-sign-in-for-easy-onboarding" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#social-sign-in-for-easy-onboarding"></a> Social Sign-in for Easy Onboarding</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/social-sign-on-PNMb1e80y5-392.avif 392w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/social-sign-on-PNMb1e80y5-392.webp 392w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Google Signin" loading="lazy" decoding="async" src="https://flowfuse.com/img/social-sign-on-PNMb1e80y5-392.jpeg" width="392" height="349" /></picture>
<em>Screenshot of Google Signin</em></p>
<p>To make it easier than ever to create a FlowFuse account, we've introduced social sign-in options that allow users to create FlowFuse Cloud accounts using their Google credentials.</p>
<p>This enhancement removes multiple steps from the onboarding process, making it significantly easier for new users to get started with FlowFuse.</p>
<h2 id="enhanced-device-agent-flow-selection" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#enhanced-device-agent-flow-selection"></a> Enhanced Device Agent Flow Selection</h2>
<p>Connecting your existing Node-RED instances running on remote devices is now even easier with the new Device Agent terminal file browser. When installing the Device Agent, you can now easily navigate your file system and select existing Node-RED instances through an interactive interface directly in the terminal.</p>
<p>This improvement replaces the previous manual file path entry process with:</p>
<ul>
<li>An intuitive file browser that works directly in your terminal</li>
<li>Easy navigation through your file system to locate flows</li>
<li>Contextual information about flows including file size and modification dates</li>
<li>A streamlined selection process that reduces setup friction</li>
</ul>
<h2 id="modernized-user-interface" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#modernized-user-interface"></a> Modernized User Interface</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/homepage-Dm8vCHIWPl-650.avif 650w, https://flowfuse.com/img/homepage-Dm8vCHIWPl-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/homepage-Dm8vCHIWPl-650.webp 650w, https://flowfuse.com/img/homepage-Dm8vCHIWPl-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/homepage-Dm8vCHIWPl-650.jpeg 650w, https://flowfuse.com/img/homepage-Dm8vCHIWPl-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of New Homepage" loading="lazy" decoding="async" src="https://flowfuse.com/img/homepage-Dm8vCHIWPl-650.jpeg" width="1300" height="632" /></picture>
<em>Screenshot of Homepage</em></p>
<p>The FlowFuse user interface was due for a restructuring in light of what users are looking for, along with a visual touchup. This release introduces a dedicated landing page that focuses on what we've learned users want to do when they first access FlowFuse. The new homepage provides:</p>
<ul>
<li>Direct access to relevant hosted and remote instances</li>
<li>Smart prioritization based on recent activity and deployments</li>
<li>Prominent display of issues requiring attention (performance alerts, errors)</li>
<li>Recent team activity overview through audit log integration</li>
<li>Improved layout and visual hierarchy</li>
<li>Updated color scheme and styling</li>
</ul>
<p>These changes make it easier to find what you are looking for within FlowFuse, address issues that may arise, and creates a more pleasant workspace.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#what's-next%3F"></a> What's Next?</h2>
<p>Our development team continues to focus on AI integration and workflow optimization. The upcoming releases will build on these foundational improvements while introducing more intelligent automation and development assistance features.</p>
<p>We're also working on expanding our Blueprint library and improving the overall development experience within the Node-RED editor, with several exciting features planned for the coming months.</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#what-else-is-new%3F"></a> What else is new?</h2>
<p>For a complete list of everything included in our 2.19 release, check out the <a href="https://github.com/FlowFuse/flowfuse/releases/">release notes</a>.</p>
<p>Your feedback continues to be invaluable in shaping FlowFuse's development. We'd love to hear your thoughts on these new features and any suggestions for future improvements. Please share your experiences or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Which of these new features are you most excited to try? Email me directly at greg@flowfuse.com - I'd love to hear from you!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest way to get started is with FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> and have your Node-RED instances running in the cloud within minutes.</p>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/flowfuse-release-2-19/#self-hosted"></a> Self-Hosted</h3>
<p>Get FlowFuse running locally in under 30 minutes using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/certified-nodes-v2/Curated Node-RED Integrations: FlowFuse Certified Nodes 2.0FlowFuse Unveils Certified Nodes Program to Reward Quality and Ensure Long-Term Support2025-07-01T00:00:00ZZJ van de Weg<p>At FlowFuse, we believe that enterprises deserve access to the best of Node-REDs
custom nodes and solutions crafted by recognized experts who bring deep domain
knowledge and professional standards to their work. That's why we are thrilled
to announce a significant evolution of our ecosystem: <strong>FlowFuse Certified Nodes
v2.0</strong>.</p>
<!--more-->
<h2 id="from-community-contribution-to-professional-partnership" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/certified-nodes-v2/#from-community-contribution-to-professional-partnership"></a> From Community Contribution to Professional Partnership</h2>
<p>The Node-RED community has always had a pasionate and thrived on the
domain-expertise of its developers. The community has created an environment of
innovation, and now we're taking the next step to support enterprise-grade
solutions that companies are building on Node-RED today.</p>
<p>Our new <strong>Certified Nodes</strong> program we hope to elevate the community. We are
establishing a professional partnership with the most skilled node developers
and domain experts in the community.</p>
<p>Here’s how it works:</p>
<ul>
<li><strong>Expertise Recognition:</strong> FlowFuse partners with developers who have
demonstrated deep expertise in their domains, and may have already published
nodes in their area of expertise. Ensuring our enterprise customers access the
most knowledgeable contributors in each field.</li>
<li><strong>Quality-Driven Selection:</strong> Our certification process identifies nodes that
excel in real-world environments, focusing on reliability, security,
up-to-date documentation and professional implementation standards.</li>
<li><strong>Comprehensive Solutions:</strong> We welcome both open-source innovations and
proprietary solutions. This approach allows experts to share their knowledge
broadly while also providing targeted solutions for specific industry needs.</li>
</ul>
<h2 id="what-does-it-take-to-become-a-certified-publisher%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/certified-nodes-v2/#what-does-it-take-to-become-a-certified-publisher%3F"></a> What Does It Take to Become a Certified Publisher?</h2>
<p>We are seeking partners who represent the pinnacle of expertise in their
respective domains. To join our certified ecosystem, we look for developers who
demonstrate:</p>
<ul>
<li><strong>Domain Expertise:</strong> Deep knowledge and proven experience in the specific
technology or industry vertical their nodes address</li>
<li><strong>Professional Standards:</strong> Commitment to enterprise-grade code quality,
comprehensive testing, and security best practices</li>
<li><strong>Continuous Innovation:</strong> Ongoing development and enhancement of features
that solve real-world enterprise challenges</li>
<li><strong>Expert Support:</strong> Ability to provide advanced technical support and guidance
that goes beyond basic troubleshooting</li>
</ul>
<p>Our certification process ensures that every node in the certified nodes
ecosystem meets these exacting standards. When enterprises choose certified
nodes, they gain access not just to code, but to the expertise and ongoing
support of recognized industry professionals. This creates a sustainable model
where expert knowledge is properly valued and enterprises receive the
reliability they require.</p>
<h2 id="why-partner-with-flowfuse%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/certified-nodes-v2/#why-partner-with-flowfuse%3F"></a> Why Partner with FlowFuse?</h2>
<p>Joining the FlowFuse Certified Nodes v2.0 program means becoming part of an
elite community of experts. Moreover, there's now a financial reward for doing
so. FlowFuse commits a fixed percentage of our FlowFuse Enterprise Platform Fee
to be distributed quarterly to the node authors.</p>
<p>If you are an expert developer or organization that has built exceptional
Node-RED nodes and are committed to maintaining the highest professional
standards, we want to collaborate with you. Let's work together to create a more
sustainable, professional ecosystem that connects enterprise users with the
industry's leading experts.</p>
<h3 id="contact-us-to-discuss-your-node-certification" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/07/certified-nodes-v2/#contact-us-to-discuss-your-node-certification"></a> Contact us to discuss your node certification</h3>
<script charset="utf-8" type="text/javascript" src="https://js-eu1.hsforms.net/forms/embed/v2.js"></script>
<script>
hbspt.forms.create({
portalId: "26586079",
formId: "6e02fe34-13c3-442b-8c27-9a12e72bba37",
region: "eu1"
});
</script>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/Part 2: Building an Andon Task Manager with FlowFuseStep-by-step guide to building a real-time issue reporting and task tracking system using FlowFuse.2025-06-26T00:00:00ZSumit Shinde<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/">Part 1</a>, we introduced the concept of an Andon Task Manager—designed to streamline issue reporting and resolution on the factory floor—and outlined the system’s key features, user roles, and dashboard layout.</p>
<!--more-->
<p>In this part 2, we move from planning to implementation. The focus now shifts to building the actual system using <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a> (Node-RED Dashboard 2.0), hosted on the FlowFuse platform. We will begin by developing the <strong>Lines view</strong> for regular users, along with a line selection menu. The Department View and Admin interface will follow in a later part of the series.</p>
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#getting-started"></a> Getting Started</h2>
<p>To simplify the development process, the implementation is divided into the following key sections:</p>
<ul>
<li>Initialize SQLite Database</li>
<li>Seed Demo Data: Departments & Lines</li>
<li>Build Line Selection Menu</li>
<li>Enable URL-Based Dashboard Access</li>
<li>Create Live Request Fetch Flow (Per Line)</li>
<li>Render Request Data in a Table</li>
<li>Setting Up Visual Alerts and Timestamp Formatting</li>
<li>Highlight Requests with CSS & Add Buttons to Update Request Status</li>
<li>Create New Request Submission Flow</li>
</ul>
<p>Before proceeding, a basic understanding of Node-RED is recommended. If you are new to Node-RED, consider going through this <a href="https://node-red-academy.learnworlds.com/course/node-red-getting-started">free Node-RED Fundamentals Course</a> to get started.</p>
<blockquote>
<p><strong>Tip:</strong> Organize your flows into clearly defined groups. For reference, images of each flow are provided. Please use the exact names given to each flow—this will help ensure consistency and make it easier to navigate back to specific flows when referenced later.</p>
</blockquote>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#prerequisites"></a> Prerequisites</h3>
<p>Before you begin building the Andon Task Manager with FlowFuse, make sure you have the following:</p>
<ul>
<li><strong>Running FlowFuse Instance:</strong> Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out the <a href="https://app.flowfuse.com/account/create">free trial</a> and <a href="https://flowfuse.com/docs/user/introduction/#creating-a-node-red-instance">learn</a> how to create an instance.</li>
<li><strong>@flowfuse/node-red-dashboard:</strong> Ensure you have FlowFuse Dashboard (also known as Node-RED Dashboard 2.0 in the community) installed.</li>
<li><strong>SQLite Contrib Node:</strong> Install <code>node-red-contrib-sqlite</code> to handle local data storage.</li>
<li><strong>FlowFuse Multi-user Andon:</strong> Install <code>@flowfuse/node-red-dashboard-2-user-addon</code> to enable multi-user support.</li>
<li><strong>Enable FlowFuse User Authentication:</strong> <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#enabling-flowfuse-user-authentication">Enable FlowFuse User Authentication</a> on your FlowFuse instance.</li>
<li><strong>Moment Contrib Node:</strong> Install <code>node-red-contrib-moment</code> for date and time formatting.</li>
</ul>
<h3 id="initialize-sqlite-database" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#initialize-sqlite-database"></a> Initialize SQLite Database</h3>
<p>The first step is to set up a database to store requests and their updates.</p>
<ol>
<li>
<p>Drag an <strong>Inject</strong> node onto the canvas and configure it to trigger on Deploy, after a delay of 0.1 seconds.</p>
</li>
<li>
<p>Drag an <strong>SQLite</strong> node onto the canvas. Double-click and click the <strong>+</strong> icon to add a new database configuration.</p>
</li>
<li>
<p>Give the database a name and set the mode to <strong>Read-Write-Create</strong>. Click <strong>Add</strong> to save.</p>
</li>
<li>
<p>Set <strong>SQL Query</strong> to <strong>Fixed statement</strong> and enter:</p>
<div style="position: relative" id="code-container-132">
<pre class="language-sql"><code id="code-132" class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> <span class="token keyword">IF</span> <span class="token operator">NOT</span> <span class="token keyword">EXISTS</span> requests <span class="token punctuation">(</span><br /> rowid <span class="token keyword">INTEGER</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span><br /> line <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span><br /> support <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span><br /> requested <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span><br /> acknowledged <span class="token keyword">TEXT</span><span class="token punctuation">,</span><br /> resolved <span class="token keyword">TEXT</span><span class="token punctuation">,</span><br /> notes <span class="token keyword">TEXT</span><br /><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-132" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Connect the <strong>Inject</strong> node to the SQLite node.</p>
</li>
<li>
<p>Click <strong>Deploy</strong>.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sqlite-create-flow-oZF5JZI41T-720.avif 720w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sqlite-create-flow-oZF5JZI41T-720.webp 720w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED flow showing an Inject node connected to an SQLite node to create a requests table in the database." loading="lazy" decoding="async" src="https://flowfuse.com/img/sqlite-create-flow-oZF5JZI41T-720.jpeg" width="720" height="114" /></picture>
<em>Flow to initialize the SQLite database and create the requests table.</em></p>
<p>Once deployed, this will create the SQLite database and <code>requests</code> table if it does not already exist.</p>
<h3 id="seed-demo-data%3A-departments-%26-lines" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#seed-demo-data%3A-departments-%26-lines"></a> Seed Demo Data: Departments & Lines</h3>
<p>As discussed in the planning section, only the admin role will have the ability to add new departments and lines. Since the admin feature is not yet available, we will populate demo data using a predefined flow to allow ourself to test the application while the standard user interface is being developed.</p>
<div id="nr-flow-178" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow178 = "\n[{\"id\":\"d5dd9bfc599374d4\",\"type\":\"tab\",\"label\":\"Populate with demo support areas and lines\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"6471824e24f18939\",\"type\":\"group\",\"z\":\"d5dd9bfc599374d4\",\"name\":\"Add demo production line and department\",\"style\":{\"label\":true},\"nodes\":[\"34c733b480e41a13\",\"4e46ae25d198fd1b\",\"882e29d2dcc9326c\",\"399bd159f46c7427\",\"5001c1cdf6661f88\"],\"x\":34,\"y\":39,\"w\":1302,\"h\":122},{\"id\":\"34c733b480e41a13\",\"type\":\"inject\",\"z\":\"d5dd9bfc599374d4\",\"g\":\"6471824e24f18939\",\"name\":\"Add production lines and department for testing\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":260,\"y\":100,\"wires\":[[\"4e46ae25d198fd1b\",\"399bd159f46c7427\"]]},{\"id\":\"4e46ae25d198fd1b\",\"type\":\"switch\",\"z\":\"d5dd9bfc599374d4\",\"g\":\"6471824e24f18939\",\"name\":\"Is lines undefined?\",\"property\":\"#:(persistent)::lines\",\"propertyType\":\"global\",\"rules\":[{\"t\":\"istype\",\"v\":\"undefined\",\"vt\":\"undefined\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":770,\"y\":80,\"wires\":[[\"882e29d2dcc9326c\"]]},{\"id\":\"882e29d2dcc9326c\",\"type\":\"change\",\"z\":\"d5dd9bfc599374d4\",\"g\":\"6471824e24f18939\",\"name\":\"Store lines to context store\",\"rules\":[{\"t\":\"set\",\"p\":\"#:(persistent)::lines\",\"pt\":\"global\",\"to\":\"[{\\\"value\\\":\\\"T1\\\",\\\"label\\\":\\\"T1\\\"},{\\\"value\\\":\\\"T2\\\",\\\"label\\\":\\\"T2\\\"}]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1140,\"y\":80,\"wires\":[[]]},{\"id\":\"399bd159f46c7427\",\"type\":\"switch\",\"z\":\"d5dd9bfc599374d4\",\"g\":\"6471824e24f18939\",\"name\":\"Is departments undefined?\",\"property\":\"#:(persistent)::departments\",\"propertyType\":\"global\",\"rules\":[{\"t\":\"istype\",\"v\":\"undefined\",\"vt\":\"undefined\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":800,\"y\":120,\"wires\":[[\"5001c1cdf6661f88\"]]},{\"id\":\"5001c1cdf6661f88\",\"type\":\"change\",\"z\":\"d5dd9bfc599374d4\",\"g\":\"6471824e24f18939\",\"name\":\"Store departments to context store\",\"rules\":[{\"t\":\"set\",\"p\":\"#:(persistent)::departments\",\"pt\":\"global\",\"to\":\"[{\\\"value\\\":\\\"Maintenance\\\",\\\"label\\\":\\\"Maintenance\\\"},{\\\"value\\\":\\\"Stores\\\",\\\"label\\\":\\\"Stores\\\"},{\\\"value\\\":\\\"Quality\\\",\\\"label\\\":\\\"Quality\\\"}]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1170,\"y\":120,\"wires\":[[]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow178.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-178') })</script>
<ol>
<li>Import the provided demo flow.</li>
<li><strong>Deploy</strong> the flow.</li>
</ol>
<p>This will store demo lines and departments in the global context as <code>global.lines</code> if not already present.</p>
<h3 id="build-line-selection-menu" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#build-line-selection-menu"></a> Build Line Selection Menu</h3>
<p>Now, let us create a new page and menu item for Production Lines. This page will list all currently available production lines, making it easier to navigate through them.</p>
<ol>
<li>Drag a <strong>ui-event</strong> node onto the canvas to detect page navigation.</li>
<li>Drag a <strong>change</strong> node and configure it to:
<ul>
<li>Set <code>msg.payload.lines</code> to <code>global.get("lines")</code></li>
</ul>
</li>
<li>Drag a <strong>ui-template</strong> node onto the canvas. Create a new page with name “Line Menu” and a group.</li>
<li>Paste the following into the template:</li>
</ol>
<div style="position: relative" id="code-container-208">
<pre class="language-html"><code id="code-208" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-container</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-row</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-col</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Production Lines<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-col</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-row</span><span class="token punctuation">></span></span><br /><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-row</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>scrollable-row<span class="token punctuation">"</span></span> <span class="token attr-name">no-gutters</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-col</span> <span class="token attr-name">v-for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(btn, index) in lines<span class="token punctuation">"</span></span> <span class="token attr-name">:key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>index<span class="token punctuation">"</span></span> <span class="token attr-name">cols</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>auto<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-btn</span> <span class="token attr-name">:href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>`/dashboard/lines?line=${btn.value}`<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>custom-btn<span class="token punctuation">"</span></span> <span class="token attr-name">rounded</span><span class="token punctuation">></span></span><br /> <br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-btn</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-col</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-row</span><span class="token punctuation">></span></span><br /><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-alert</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>selectedLine<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>success<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mt-3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> Selected Line: <br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-alert</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-container</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">selectedLine</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">lines</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function">updateButtonContent</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>lines <span class="token operator">=</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>lines <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> a<span class="token punctuation">.</span>label<span class="token punctuation">.</span><span class="token function">localeCompare</span><span class="token punctuation">(</span>b<span class="token punctuation">.</span>label<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>lines <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>$socket<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'msg-input:'</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">msg</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">updateButtonContent</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name">scoped</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="token selector">.scrollable-row</span> <span class="token punctuation">{</span><br /> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br /> <span class="token property">overflow-x</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span><br /> <span class="token property">padding</span><span class="token punctuation">:</span> 10px 0<span class="token punctuation">;</span><br /> <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token selector">.scrollable-row .v-col</span> <span class="token punctuation">{</span><br /> <span class="token property">flex-shrink</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br /> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token selector">.scrollable-row</span> <span class="token punctuation">{</span><br /> <span class="token property">min-height</span><span class="token punctuation">:</span> 60px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token selector">.custom-btn</span> <span class="token punctuation">{</span><br /> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>32<span class="token punctuation">,</span> 44<span class="token punctuation">,</span> 52<span class="token punctuation">)</span> <span class="token important">!important</span><span class="token punctuation">;</span><br /> <span class="token property">color</span><span class="token punctuation">:</span> white <span class="token important">!important</span><span class="token punctuation">;</span><br /> <span class="token property">margin-right</span><span class="token punctuation">:</span> 12px<span class="token punctuation">;</span><br /> <span class="token property">padding</span><span class="token punctuation">:</span> 8px 16px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token selector">.custom-btn:hover</span> <span class="token punctuation">{</span><br /> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>54<span class="token punctuation">,</span> 70<span class="token punctuation">,</span> 86<span class="token punctuation">)</span> <span class="token important">!important</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-208" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Deploy the flow.</li>
</ol>
<p>Once deployed, the dashboard will show buttons for each production line. Clicking a line redirects the user to a page with following url:</p>
<pre><code>https://<your-instance-name>/dashboard/lines?line=T1
</code></pre>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/menu-for-lines-flow-tdK8C_Mvay-842.avif 842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/menu-for-lines-flow-tdK8C_Mvay-842.webp 842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED editor showing the flow setup for generating a menu of production lines using ui-event, change, and ui-template nodes." loading="lazy" decoding="async" src="https://flowfuse.com/img/menu-for-lines-flow-tdK8C_Mvay-842.jpeg" width="842" height="156" /></picture>
<em>Node-RED flow to create a dynamic menu for production lines on the dashboard.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/menu-for-lines-bZUuHzprtn-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/menu-for-lines-bZUuHzprtn-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Dashboard displaying styled buttons for each production line, enabling quick navigation to specific line pages." loading="lazy" decoding="async" src="https://flowfuse.com/img/menu-for-lines-bZUuHzprtn-1920.jpeg" width="1920" height="468" /></picture>
<em>Dashboard view showing production line buttons generated from the flow.</em></p>
<p>The "line" URL parameter will be used in the next section to store the user's selected production line.</p>
<h3 id="enable-url-based-dashboard-access" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#enable-url-based-dashboard-access"></a> Enable URL-Based Dashboard Access</h3>
<p>In this section, we’ll build a flow that allows users to directly access the dashboard using a URL with a line parameter (e.g., ?line=T1). The flow will validate this parameter and store the selected line for each user session. If the parameter is missing or invalid, the user will be redirected to a Not Found page.</p>
<p>To achieve this, we need to:</p>
<ul>
<li>Configure the dashboard to expose client-specific metadata.</li>
<li>Create a flow that validates the line parameter from the URL.</li>
<li>Store the user’s selected line using session-aware context data.</li>
</ul>
<h4 id="configuring-dashboard-widgets-to-include-client-information" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#configuring-dashboard-widgets-to-include-client-information"></a> Configuring Dashboard Widgets to Include Client Information</h4>
<p>To ensure client data is available in your flows, follow these steps:</p>
<ol>
<li>Open the <strong>Dashboard 2.0</strong> sidebar.</li>
<li>Switch to the <strong>Client Data</strong> tab.</li>
<li>Enable the option <strong>“Include client data”</strong>.</li>
<li>Tick the checkbox in front of:
<ul>
<li>ui-control</li>
<li>ui-template</li>
<li>ui-button</li>
<li>ui-text-input</li>
<li>ui-dropdown</li>
<li>ui-notification</li>
</ul>
</li>
<li><strong>Deploy</strong> the updated configuration.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/client-data-configuration-3DCI5h0oKW-306.avif 306w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/client-data-configuration-3DCI5h0oKW-306.webp 306w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Enabling client data in the Dashboard 2.0 settings to include metadata from specific widgets." loading="lazy" decoding="async" src="https://flowfuse.com/img/client-data-configuration-3DCI5h0oKW-306.jpeg" width="306" height="734" /></picture>
<em>Dashboard 2.0 settings to include client metadata from selected widgets like buttons and templates.</em></p>
<p>With this setting enabled, the selected widgets will include client-related metadata in their output messages under the <code>msg._client</code> property. This metadata is essential for building session-aware features in the Dashboard.</p>
<h4 id="building-a-dashboard-flow-for-url-access%2C-line-validation%2C-and-user-selection" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#building-a-dashboard-flow-for-url-access%2C-line-validation%2C-and-user-selection"></a> Building a Dashboard Flow for URL Access, Line Validation, and User Selection</h4>
<p>In this section, we'll build a Node-RED flow that handles dashboard access via direct URLs, validates the line parameter, and stores the selected line per user session using client metadata. This ensures that each user's line selection is tracked independently and that invalid or missing parameters are handled gracefully.</p>
<p>The flow performs the following key tasks:</p>
<ul>
<li>Detects when a user accesses the dashboard using a URL containing a line parameter.</li>
<li>Validates the parameter against a list of predefined production lines.</li>
<li>Stores the selected line using session-aware (client-specific) data.</li>
<li>Redirects the user appropriately based on the validity of the parameter.</li>
</ul>
<p><strong>Steps to Build the Flow:</strong></p>
<ol>
<li>
<p>Drag a <strong>ui-event node</strong> onto the canvas and configure it with the correct UI base path.</p>
</li>
<li>
<p>Drag a <strong>switch node</strong> and name it "Is it a pageview event?" and configure it with the property <code>msg.topic</code> and add the following condition:</p>
<ul>
<li><code>== $pageview</code></li>
</ul>
</li>
<li>
<p>Drag another <strong>switch node</strong> and name it "Has line key?" and configure it with the property <code>msg.payload.page.params.line</code>, with two conditions:</p>
<ul>
<li>has key <code>line</code></li>
<li>is empty</li>
</ul>
</li>
<li>
<p>Connect the <strong>ui-event node</strong> to the first switch node (labeled Is it a pageview event?). Then, connect the first output of this switch node to the second switch node (labeled Has line key?). This checks whether the page was accessed via URL and if a line parameter is present.</p>
</li>
<li>
<p>Drag a <code>function</code> node and name it "Extract Labels from Lines" and paste the following code:</p>
<div style="position: relative" id="code-container-404">
<pre class="language-javascript"><code id="code-404" class="language-javascript"><span class="token keyword">let</span> lines <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'lines'</span><span class="token punctuation">,</span> <span class="token string">'persistent'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token keyword">let</span> labels <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token operator">...</span>lines<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">obj</span> <span class="token operator">=></span> obj<span class="token punctuation">.</span>label<span class="token punctuation">)</span><span class="token punctuation">,</span><br /><span class="token punctuation">]</span><span class="token punctuation">;</span><br />msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>labels <span class="token operator">=</span> labels<span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-404" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ol>
<p>This retrieves the list of available lines from the persistent global context, extracts their labels, and creates an array for easier verification of whether the user selected line is present.</p>
<ol start="6">
<li>Drag a <strong>change node</strong> and name it "Store line selection":
<ul>
<li>Set <code>global.store[msg._client.socketId].line</code> to <code>msg.payload</code>.</li>
</ul>
</li>
<li>Drag another <strong>switch node</strong> and name it "Is the currently accessed page 'Lines'?":</li>
</ol>
<ul>
<li>is equal to "Lines"</li>
</ul>
<ol start="8">
<li>Connect the <strong>function node</strong> (Extract Labels from Lines) and the <strong>change node</strong> (Store line selection) to the first output of the <strong>switch node</strong> (Has line key?).</li>
<li>Drag another <code>switch</code> node and name it "Is line valid?" and set the property to <code>msg.payload.labels</code> and add following conditions:
<ul>
<li>contains <code>global.store[msg._client.socketId].line</code></li>
</ul>
</li>
<li>For the first output of the <code>switch</code> node (Is the currently accessed page 'Lines'?), drag a <strong>change</strong> node and name it "Redirect the user to the All Lines menu" and Configure it to:
<ul>
<li>Set <code>msg.payload</code> to <code>"All Lines"</code></li>
</ul>
</li>
<li>Drag a <strong>ui-control node</strong> onto the canvas and connect it to the <strong>change node</strong> (Redirect the user to the All Lines menu). This node will handle the redirection or display feedback on the dashboard.</li>
</ol>
<p>This checks whether the selected line is valid by comparing it with the list of known line labels.</p>
<ol start="12">
<li>For the second output of the switch node (Is line valid?), drag a switch node and give it name "Is the currently accessed page 'Lines'?" and Set property to <code>msg.payload.page.name</code> and add following condition to check against:</li>
</ol>
<ul>
<li>== "Lines"</li>
<li>Otherwise</li>
</ul>
<ol start="13">
<li>For the first output of the switch node ((Is the currently accessed page 'Lines'?), drag a change node and configure it to:
- Set <code>msg.payload</code> to "Incorrect Link"</li>
<li>Drag a ui-control node onto the canvas and configure it with the correct UI base path and connect it the change node (Redirect to Not found page).</li>
<li>Deploy the changes.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/accessing-production-lines-y86f2_ovVa-1820.avif 1820w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/accessing-production-lines-y86f2_ovVa-1820.webp 1820w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow for handling dashboard navigation, validating URL parameters, and assigning selected production lines to individual client sessions." loading="lazy" decoding="async" src="https://flowfuse.com/img/accessing-production-lines-y86f2_ovVa-1820.jpeg" width="1820" height="188" /></picture>
<em>Flow for managing URL-based access, validating line parameters, and storing client-specific selections on the OEE dashboard.</em></p>
<h3 id="create-live-request-flow-(per-line)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#create-live-request-flow-(per-line)"></a> Create Live Request Flow (Per Line)</h3>
<p>Let’s build the flow to retrieve the data now based on the user selection.</p>
<ol>
<li>
<p>Drag a <code>Template</code> widget onto the canvas.</p>
</li>
<li>
<p>Double-click the Template widget to open its configuration.</p>
</li>
<li>
<p>Set the scope to <code>ui</code> and select the appropriate UI Base.</p>
</li>
<li>
<p>Paste the following script into the content field. This script triggers every second and sends a message that includes the current user's client data:</p>
<div style="position: relative" id="code-container-540">
<pre class="language-html"><code id="code-540" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Set an interval to update the message every second</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>intervalId <span class="token operator">=</span> <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token string">'Component has loaded'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">beforeUnmount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Clear the interval when the component is about to be destroyed</span><br /> <span class="token function">clearInterval</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>intervalId<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-540" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag a <code>Change</code> node and name it "Set params".</p>
</li>
<li>
<p>Configure the Change node with the following rules:</p>
<ul>
<li>Set <code>msg.params.$line</code> to <code>global.store[msg._client.socketId].line</code></li>
<li>Set <code>msg.query</code> to <code>"line"</code></li>
</ul>
</li>
<li>
<p>Drag a <code>Template</code> node onto the canvas.</p>
</li>
<li>
<p>Double-click the Template node and set the property to <code>msg.topic</code>.</p>
</li>
<li>
<p>Paste the following SQL query into the content field:</p>
<div style="position: relative" id="code-container-578">
<pre class="language-sql"><code id="code-578" class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> requests <span class="token keyword">WHERE</span> <span class="token string">""</span> <span class="token operator">=</span> <span class="token string">""</span> <span class="token operator">AND</span> resolved <span class="token operator">IS</span> <span class="token boolean">NULL</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-578" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag a <code>Markdown</code> widget and name it "Show currently selected line".</p>
</li>
<li>
<p>Create a new group in the UI for the Markdown widget to render into.</p>
</li>
<li>
<p>Enter the following content into the Markdown widget:</p>
<div style="position: relative" id="code-container-594">
<pre class="language-html"><code id="code-594" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Line: <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-594" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/lines-page-with-tittle-of-selected-line-xkrPuI2RPC-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/lines-page-with-tittle-of-selected-line-xkrPuI2RPC-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Dashboard view showing the title of the selected production line centered at the top." loading="lazy" decoding="async" src="https://flowfuse.com/img/lines-page-with-tittle-of-selected-line-xkrPuI2RPC-1920.jpeg" width="1920" height="613" /></picture>
<em>The dashboard displays the selected production line name, retrieved from the client context, rendered using a Markdown widget.</em></p>
<ol start="13">
<li>Drag a <code>SQLite</code> node onto the canvas.</li>
<li>Select the appropriate database configuration.</li>
<li>Set the node to use the SQL query via "Prepared Statement".</li>
<li>Connect the <code>Template</code> widget to <code>Change</code> node and <code>Template</code> node to <code>SQLite</code> node.</li>
<li>Drag a <code>link-out</code> node onto the canvas and connect it to the <code>SQLite</code> node.</li>
<li>Deploy the changes.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/retrive-data-IeZVstEfKG-1868.avif 1868w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/retrive-data-IeZVstEfKG-1868.webp 1868w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED flow to fetch live requests for a selected production line using client context and SQLite query." loading="lazy" decoding="async" src="https://flowfuse.com/img/retrive-data-IeZVstEfKG-1868.jpeg" width="1868" height="272" /></picture>
<em>Flow that triggers periodic queries for open requests specific to the user’s selected line using the client session and a SQLite database.</em></p>
<h3 id="preparing-data-rendering-it-in-a-table" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#preparing-data-rendering-it-in-a-table"></a> Preparing Data Rendering it in a Table</h3>
<p>Once the data is retrieved, it needs to be validated, formatted, and routed appropriately for display. In this section, a flow will be built to check whether any unresolved requests exist for the selected production line. If there are no requests, a message will be shown to the user. Otherwise, the data will be processed and rendered in a table format using Dashboard widgets.</p>
<ol>
<li>
<p>Drag a <strong>link-in node</strong> onto the canvas and connect it to the last <strong>link-out node</strong>.</p>
</li>
<li>
<p>Add a <strong>switch node</strong> to check whether the <code>msg.payload</code> is empty, name it "Is Payload empty?".</p>
<ul>
<li>Configure the switch with the following conditions:
<ul>
<li><code>msg.payload is empty</code></li>
<li><code>Otherwise</code></li>
</ul>
</li>
</ul>
</li>
<li>
<p>Drag a <strong>change node</strong>, name it "Show 'no outstanding request' message" and configure it to set a message when the payload is empty:</p>
<ul>
<li>Set <code>msg.payload</code> to <code>"There are no outstanding requests"</code></li>
</ul>
</li>
<li>
<p>Connect this <strong>change node</strong> to the first output of the switch node ("Is Payload empty?").</p>
</li>
<li>
<p>Drag another <strong>change node</strong>, name it "Remove 'no outstanding request' message" and configure it as follows:</p>
<ul>
<li>Set <code>msg.payload</code> to an empty string <code>""</code></li>
</ul>
</li>
<li>
<p>Connect this second <strong>change node</strong> to the second output of the switch node ("Is Payload empty?").</p>
</li>
<li>
<p>Drag a <strong>text widget</strong>, name it "Text Widget for Message", onto the canvas, double-click it, and add a new group in the "Lines" page to render the message.</p>
</li>
<li>
<p>Connect the <strong>text widget</strong> ("Text Widget for Message") to both <strong>change nodes</strong> that are setting the text message ("Show 'no outstanding request' message" and "Remove 'no outstanding request' message").</p>
</li>
<li>
<p>Drag another <strong>link-out node</strong> onto the canvas and connect it to the second output of the switch node ("Is Payload empty?") that checks whether <code>msg.payload</code> is empty.</p>
</li>
<li>
<p>Drag another <strong>link-in</strong> onto the canvas.</p>
</li>
<li>
<p>Drag a <strong>split node</strong>, name it "Split Node", onto the canvas and connect it to the <strong>link-in node</strong>.</p>
</li>
<li>
<p>Drag a <strong>link-out node</strong> and connect it to the <strong>split node</strong>.</p>
</li>
<li>
<p>Deploy the changes.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/preparing-data-iKZ2_PR2i_-822.avif 822w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/preparing-data-iKZ2_PR2i_-822.webp 822w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow that checks if unresolved requests exist, sends an appropriate message when none are found, or prepares the data for tabular rendering" loading="lazy" decoding="async" src="https://flowfuse.com/img/preparing-data-iKZ2_PR2i_-822.jpeg" width="822" height="194" /></picture>
<em>Flow that checks if unresolved requests exist, sends an appropriate message when none are found, or prepares the data for tabular rendering.</em></p>
<h3 id="setting-up-visual-alerts-and-timestamp-formatting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#setting-up-visual-alerts-and-timestamp-formatting"></a> Setting Up Visual Alerts and Timestamp Formatting</h3>
<p>To enhance the visibility of production line requests, this section focuses on setting up visual alerts based on the age of each request and formatting timestamps in a user-friendly way. The created timestamp is always shown using relative time (e.g., "5 minutes ago"). For acknowledged and resolved, relative formatting is applied only when those timestamps are available. This improves readability and makes it easier to identify requests that are pending action.</p>
<ol>
<li>
<p>Drag a <strong>link-in node</strong> and a <strong>function node</strong> onto the canvas. Name the <strong>function node</strong> as "Highlight Old Requests" and open it.</p>
</li>
<li>
<p>Paste the following JavaScript code into the <strong>function node</strong>:</p>
</li>
</ol>
<div style="position: relative" id="code-container-762">
<pre class="language-javascript"><code id="code-762" class="language-javascript"><span class="token keyword">const</span> requested <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>requested<span class="token punctuation">;</span><br /><span class="token keyword">const</span> now <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> requestedTime <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>requested<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> difference <span class="token operator">=</span> now <span class="token operator">-</span> requestedTime<span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> oldRequestThreshold <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'oldRequestThreshold'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> veryOldRequestThreshold <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'veryOldRequestThreshold'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">const</span> alertActivationThreshold <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'alertActivationThreshold'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>difference <span class="token operator">></span> <span class="token punctuation">(</span>veryOldRequestThreshold <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>class <span class="token operator">=</span> <span class="token string">'older'</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>difference <span class="token operator">></span> <span class="token punctuation">(</span>alertActivationThreshold <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>alert <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">return</span> msg<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>difference <span class="token operator">></span> <span class="token punctuation">(</span>oldRequestThreshold <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>class <span class="token operator">=</span> <span class="token string">'old'</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>difference <span class="token operator">></span> <span class="token punctuation">(</span>alertActivationThreshold <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>alert <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">return</span> msg<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>difference <span class="token operator">></span> <span class="token punctuation">(</span>alertActivationThreshold <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>alert <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>class <span class="token operator">=</span> <span class="token string">'normal'</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> msg<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-762" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the link-in node to the function node.</li>
<li>Drag a date time formatter node onto the canvas. Name it "Format Requested Time" and double-click it to configure.
<ul>
<li>Set outputFrom to fromNow.</li>
<li>Set both input and output to <code>msg.payload.requested</code>.</li>
</ul>
</li>
<li>Connect the function node to the date time formatter node.</li>
<li>Drag a link-out node and connect it to the function node.</li>
<li>Drag a switch node onto the canvas. Name it "Check if Acknowledged is null". Set the property to <code>msg.payload.acknowledged</code>, and add the following conditions:
<ul>
<li>is null</li>
<li>Otherwise</li>
</ul>
</li>
<li>Connect the function node to the switch node.</li>
<li>Drag a second date time formatter node onto the canvas. Name it "Format Acknowledged Time".
<ul>
<li>Set outputFrom to fromNow.</li>
<li>Set both input and output to <code>msg.payload.acknowledged</code>.</li>
</ul>
</li>
<li>Connect the second date time formatter node to the second output of the switch node ("Check if Acknowledged is null").</li>
<li>Drag another switch node onto the canvas. Name it "Check if Resolved is null". set the property to <code>msg.payload.resolved</code>. Add the following conditions:</li>
</ol>
<ul>
<li>is null</li>
<li>Otherwise</li>
</ul>
<ol start="12">
<li>Connect this switch node ("Check if Resolved is null") to the output of the second date time formatter ("Format Acknowledged Time") node and to the first output of the first switch node ("Check if Acknowledged is null").</li>
<li>Drag a third date time formatter node onto the canvas. Name it "Format Resolved Time" and set outputFrom to fromNow. Set both input and output to <code>msg.payload.resolved</code>.</li>
<li>Connect this third date time formatter node to the second output of the second switch node ("Check if Resolved is null") .</li>
<li>Drag a link-out node and connect it to the third date time formatter node.</li>
<li>Drag another link-out node and connect it to the first output of the second switch node. Name it "Link to First Switch Output".</li>
<li>Deploy the changes.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/highlighted-requst-mjzhUzRAFX-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/highlighted-requst-mjzhUzRAFX-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Dashboard view displaying a highlighted request entry with visual emphasis based on request age." loading="lazy" decoding="async" src="https://flowfuse.com/img/highlighted-requst-mjzhUzRAFX-1920.jpeg" width="1920" height="82" /></picture>
<em>Dashboard showing a request visually highlighted based on how long ago it was made, with applied styling and alert classification.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/visual-alert-and-timestamp-formatting-Ri1AhBpAAc-1306.avif 1306w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/visual-alert-and-timestamp-formatting-Ri1AhBpAAc-1306.webp 1306w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED flow that includes nodes for assigning visual alert classes and formatting timestamps using relative time." loading="lazy" decoding="async" src="https://flowfuse.com/img/visual-alert-and-timestamp-formatting-Ri1AhBpAAc-1306.jpeg" width="1306" height="104" /></picture>
<em>Flow for setting visual alert classes and formatting timestamps like "5 minutes ago" to enhance clarity and urgency of displayed requests.</em></p>
<h2 id="highlight-requests-with-css-%26-add-buttons-to-update-request-status" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#highlight-requests-with-css-%26-add-buttons-to-update-request-status"></a> Highlight Requests with CSS & Add Buttons to Update Request Status</h2>
<p>We have the data prepared, the class property added to each request message, and the timestamp formatted for better readability. In this section, we will add <strong>'Resolve'</strong> and <strong>'Acknowledge'</strong> buttons for each request to update its status and apply CSS classes based on the <code>status</code> property for visual highlighting.</p>
<ol>
<li>
<p>Drag a <strong>link-in</strong> node onto the canvas and connect it to the last <strong>link-out</strong> node.</p>
</li>
<li>
<p>Drag a switch node, name it "Is acknowledged null?", and set the property to <code>msg.payload.acknowledged</code>. Add the following conditions:</p>
<ul>
<li>is null</li>
<li>otherwise</li>
</ul>
</li>
<li>
<p>Drag a <strong>template node</strong>, name it <strong>"Build ack link"</strong>, set the property to <code>msg.payload.acknowledged</code>, and add the following Mustache:</p>
<div style="position: relative" id="code-container-929">
<pre class="language-html"><code id="code-929" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/dashboard/lines?line=&action=ack&request=<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">color</span><span class="token punctuation">:</span> #000000</span><span class="token punctuation">"</span></span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ACKNOWLEDGE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-929" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Connect the <strong>link-in</strong> node to the <strong>switch</strong> node. Connect the <strong>first output</strong> of the switch node ("Is acknowledged null?") to the input of the <strong>template</strong> node.</p>
</li>
<li>
<p>Drag another switch node, name it "Is resolved null?", set the property to <code>msg.payload.resolved</code>, and add the following conditions:</p>
<ul>
<li>is null</li>
<li>otherwise</li>
</ul>
</li>
<li>
<p>Drag a <strong>template node</strong>, give it name "Build res link", set the property to <code>msg.payload.resolved</code>, and add the following Mustache:</p>
<div style="position: relative" id="code-container-957">
<pre class="language-html"><code id="code-957" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/dashboard/lines?line=&action=res&request=<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">color</span><span class="token punctuation">:</span> #000000</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>RESOLVE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-957" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Connect the input of this second switch node ("Is resolved null?") to the <strong>second output</strong> of the previous switch node (<code>acknowledged</code> switch), then connect the <strong>first output</strong> of the <code>resolved</code> switch node to the input of the second <strong>template</strong> node.</p>
</li>
<li>
<p>Drag a <strong>link-out</strong> node and connect both outputs of the <code>resolved</code> switch node to this link-out.</p>
</li>
<li>
<p>Deploy the changes.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/add-request-update-button-jlyNLpUPw2-1212.avif 1212w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/add-request-update-button-jlyNLpUPw2-1212.webp 1212w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Adding 'Acknowledged' and 'Resolved' buttons for each request in the table" loading="lazy" decoding="async" src="https://flowfuse.com/img/add-request-update-button-jlyNLpUPw2-1212.jpeg" width="1212" height="118" /></picture>
<em>Adding action buttons to update the status of each request directly from the dashboard table.</em></p>
<h3 id="adding-a-mechanism-to-update-the-status-of-a-request-in-the-database" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#adding-a-mechanism-to-update-the-status-of-a-request-in-the-database"></a> Adding a Mechanism to Update the Status of a Request in the Database</h3>
<ol>
<li>
<p>Drag <strong>ui event widget</strong> onto the canvas and configure it with the correct ui base.</p>
</li>
<li>
<p>Drag <strong>change node</strong> onto the canvas and add the following element:</p>
<ul>
<li>Set <code>msg.params</code> to <code>{}</code></li>
<li>Set <code>msg.params.$request</code> to <code>msg.payload.query.request</code></li>
</ul>
</li>
<li>
<p>Drag <strong>Date/Time Formatter node</strong> onto the canvas and set input format to "timestamp: milliseconds since epoch" and output to <code>msg.now</code>.</p>
</li>
<li>
<p>Drag <strong>switch node</strong> onto the canvas, set property to <code>msg.query.action</code>, and add the following conditions to check against:</p>
<ul>
<li>== ack</li>
<li>== res</li>
<li>otherwise</li>
</ul>
</li>
<li>
<p>Drag two SQLite nodes onto the canvas, select the correct database configuration for both, and set the query type to 'Prepared Statement'.</p>
</li>
<li>
<p>For the <strong>first</strong>, set the following sql query:</p>
<div style="position: relative" id="code-container-1040">
<pre class="language-sql"><code id="code-1040" class="language-sql"><span class="token keyword">UPDATE</span> requests <br /><span class="token keyword">SET</span> acknowledged <span class="token operator">=</span> $now<br /><span class="token keyword">WHERE</span> rowid <span class="token operator">=</span> $request <br /><span class="token operator">AND</span> acknowledged <span class="token operator">IS</span> <span class="token boolean">NULL</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-1040" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>For the <strong>second</strong>, set the following sql query:</p>
<div style="position: relative" id="code-container-1046">
<pre class="language-sql"><code id="code-1046" class="language-sql"><span class="token keyword">UPDATE</span> requests <br /><span class="token keyword">SET</span> resolved <span class="token operator">=</span> $now<br /><span class="token keyword">WHERE</span> rowid <span class="token operator">=</span> $request <br /><span class="token operator">AND</span> resolved <span class="token operator">IS</span> <span class="token boolean">NULL</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-1046" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Connect the <strong>ui event widget</strong> to <strong>change node</strong>, <strong>change node</strong> to <strong>date/time formatter node</strong>, <strong>date/time formatter</strong> to <strong>switch node</strong>, and:</p>
</li>
</ol>
<ul>
<li><strong>switch node first output</strong> to first <strong>first sqlite node</strong></li>
<li><strong>switch node second output</strong> to second <strong>second sqlite node</strong></li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/update-request-status-9iDtTbpsR6-1556.avif 1556w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/update-request-status-9iDtTbpsR6-1556.webp 1556w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow for the mechanism to update request status" loading="lazy" decoding="async" src="https://flowfuse.com/img/update-request-status-9iDtTbpsR6-1556.jpeg" width="1556" height="202" /></picture><br />
<em>Flow that handles the update of request status based on user actions (Acknowledged or Resolved).</em></p>
<h3 id="render-request-data-in-a-table" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#render-request-data-in-a-table"></a> Render Request Data in a Table</h3>
<p>Now, let's display the prepared data in a table. To do this, we'll use a <strong>ui_table</strong> widget. However, before displaying, we need to convert the data back into an array, as we are currently spilling array data retrieved from the database into a single message.</p>
<ol>
<li>
<p>Drag a <strong>link-in</strong> node onto the canvas and connect it to the <strong>last link-out</strong> node.</p>
</li>
<li>
<p>Drag a <strong>join</strong> node onto the canvas and connect it to the <strong>link-in</strong> node. Double-click on the join node and set the mode to <strong>automatic</strong>.</p>
</li>
<li>
<p>Drag another <strong>link-out</strong> node and connect it to the <strong>join</strong> node.</p>
</li>
<li>
<p>Drag a <strong>link-in</strong> node and connect it to the <strong>last link-out</strong> node.</p>
</li>
<li>
<p>Drag a <strong>ui_table</strong> widget onto the canvas and double-click to configure.</p>
</li>
<li>
<p>Create a <strong>new group</strong> on the <em>lines</em> page.</p>
</li>
<li>
<p>Set <strong>Action</strong> to <code>replace</code> and <strong>Interaction</strong> to <code>none</code>.</p>
</li>
<li>
<p>Untick the <strong>Auto Calculate Columns</strong> option.</p>
</li>
<li>
<p>Add the following column elements:</p>
<ul>
<li>Key: rowid, Label: Request, Align: Left, Type: Text</li>
<li>Key: line, Label: Line, Align: Left, Type: Text</li>
<li>Key: support, Label: Support, Align: Left, Type: Text</li>
<li>Key: requested, Label: Requested, Align: Left, Type: HTML</li>
<li>Key: acknowledged, Label: Acknowledged, Align: Left, Type: HTML</li>
<li>Key: resolved, Label: Resolved, Align: Left, Type: HTML</li>
<li>Key: notes, Label: Notes, Align: Left, Type: Text</li>
</ul>
</li>
<li>
<p>Deploy the changes.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/render-data-in-table-oMd-l-omTm-554.avif 554w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/render-data-in-table-oMd-l-omTm-554.webp 554w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Render Data on table"" loading="lazy" decoding="async" src="https://flowfuse.com/img/render-data-in-table-oMd-l-omTm-554.jpeg" width="554" height="200" /></picture>
<em>Render Data on table.</em></p>
<h2 id="create-new-request-submission-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#create-new-request-submission-flow"></a> Create New Request Submission Flow</h2>
<p>To create a flow that allows users to submit a request, follow these steps to set up the necessary UI elements, store the request details, validate input, and store the data in a database.</p>
<ol>
<li>
<p>Drag the <strong>ui-event</strong> widget onto the canvas and configure it with the correct UI settings.</p>
</li>
<li>
<p>Drag a <strong>change node</strong> to retrieve the department list and name it "Show Dropdown Options". Add the following:</p>
<ul>
<li>Set <code>msg.ui_update.options</code> to <code>global.departments</code>.</li>
</ul>
</li>
<li>
<p>Create a new group for the dropdown widget on the lines page. Connect the <strong>change node</strong> to the input of the dropdown widget, then link it to the <strong>ui-event</strong> node.</p>
</li>
<li>
<p>Drag another <strong>change node</strong> and set <code>msg.payload</code> to <code>msg.store[msg._client.socketId].support</code>. Name it "Store support (department) to context store".</p>
</li>
<li>
<p>Drag a <strong>text input widget</strong> for the notes field. Create a group for it in the lines page.</p>
</li>
<li>
<p>Add another <strong>change node</strong> to store the notes in the global context and name it "Store notes to context store":</p>
<ul>
<li>Set <code>msg.payload</code> to <code>msg.store[msg._client.socketId].notes</code>.</li>
</ul>
</li>
<li>
<p>Connect the change node ("Store support (department) to context store") to the input of the dropdown widget and connect the change node ("Store notes to context store") to the text input widget.</p>
</li>
<li>
<p>Drag the <strong>button widget</strong>, label it "Request Support," and connect it to the <strong>change node</strong> that updates the UI with the department list.</p>
</li>
<li>
<p>Add a <strong>change node</strong> to store the request details in the global context and name it "Retrieve entered support request data":</p>
<ul>
<li>Set <code>msg.request</code> to <code>{}</code>.</li>
<li>Set <code>msg.request.support</code> to the value <code>msg.store[msg._client.socketId].support</code>.</li>
<li>Set <code>msg.request.notes</code> to the value <code>msg.store[msg._client.socketId].notes</code>.</li>
<li>Set <code>msg.request.line</code> to the value <code>msg.store[msg._client.socketId].line</code>.</li>
<li>Set <code>msg.request.reference</code> to the value <code>msg.store[msg._client.socketId].reference</code>.</li>
</ul>
</li>
<li>
<p>Drag the switch node onto the canvas and set property to <code>msg.request</code> and add condtion to check "is not null".</p>
</li>
<li>
<p>Drag a <strong>function node</strong> and add the following code. Name it "Does department and notes are not empty?":</p>
</li>
</ol>
<div style="position: relative" id="code-container-1271">
<pre class="language-javascript"><code id="code-1271" class="language-javascript"><span class="token keyword">let</span> request <span class="token operator">=</span> msg<span class="token punctuation">.</span>request<span class="token punctuation">;</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> request <span class="token operator">!==</span> <span class="token string">"object"</span> <span class="token operator">||</span> request <span class="token operator">===</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token string">"Request must be an object."</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> msg<span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>request<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">"support"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token string">"Please select the appropriate department for the request."</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token keyword">null</span><span class="token punctuation">,</span> msg<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>request<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">"notes"</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token keyword">typeof</span> request<span class="token punctuation">.</span>notes <span class="token operator">!==</span> <span class="token string">"string"</span> <span class="token operator">||</span> request<span class="token punctuation">.</span>notes<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token string">"Please add notes to provide more context on the request."</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> msg<span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">[</span>msg<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-1271" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="12">
<li>
<p>Connect the change node to switch node and switch node to function node. Connect the function node's first output to a <strong>Date/Time Formatter</strong> node. Set the input to 'Timestamp (milliseconds since epoch)' and the output to <code>msg.request.time</code>.</p>
</li>
<li>
<p>Drag a <strong>change node</strong> and set <code>msg.payload</code> to "Are you sure you want to submit a request?". Name it "Set Confirmation message".</p>
</li>
<li>
<p>Connect the change node ("Set Confirmation message") to the <strong>Date/Time Formatter</strong> node.</p>
</li>
<li>
<p>Drag a <strong>ui-notification widget</strong>, configure it with the correct UI, and set the position to center, checked the checkbox for both allow mnaual dismisal and all amnual confirmation, change close to "Cancel".</p>
</li>
<li>
<p>Connect the change node ("Set Confirmation message") to the <strong>ui-notification</strong> widget.</p>
</li>
<li>
<p>Drag another <strong>ui-notification widget</strong>, configure it with the correct UI, and set the position to center.</p>
</li>
<li>
<p>connect it to the second output of the function node.</p>
</li>
<li>
<p>Add a <strong>switch node</strong> and set the property to <code>msg.payload</code> with the condition:</p>
</li>
</ol>
<ul>
<li>== <code>confirm_clicked</code>
Name it "Is confirm clicked?"</li>
</ul>
<ol start="20">
<li>Drag a <strong>change node</strong> and add the following elements and give it name "Set Params":</li>
</ol>
<ul>
<li>Set <code>msg.params</code> to <code>{}</code>.</li>
<li>Set <code>msg.params.$line</code> to the value <code>msg.request.linet</code>.</li>
<li>Set <code>msg.params.$support</code> to the value <code>msg.request.support</code>.</li>
<li>Set <code>msg.params.$time</code> to the value <code>msg.request.time</code>.</li>
<li>Set <code>msg.params.$notes</code> to the value <code>msg.request.notes</code>.</li>
</ul>
<ol start="21">
<li>
<p>Connect the change node ("Set Params") to the switch node ("Is confirm clicked?").</p>
</li>
<li>
<p>Drag a <strong>sqlite node</strong>, select the correct database configuration, and choose SQL Query via "Prepared Statement" and connect that sqlite node to the input change node ("Set Params")</p>
</li>
<li>
<p>Drag a <strong>change node</strong> and set the following, give it name "Clear entered request data":</p>
</li>
</ol>
<ul>
<li>Delete <code>msg.store[msg._client.socketId].support</code>.</li>
<li>Delete <code>msg.store[msg._client.socketId].notes</code>.</li>
</ul>
<ol start="24">
<li>
<p>Connect the <strong>change node</strong>* ("Clear entered request data") to the <strong>sqlite node</strong>.</p>
</li>
<li>
<p>Drag a <strong>link-out node</strong> and connect it to the last change node.</p>
</li>
<li>
<p>Drag a <strong>link-in node</strong> and connect it to the last link-out node.</p>
</li>
<li>
<p>Drag two <strong>change nodes</strong> and configure them as follows:</p>
</li>
</ol>
<ul>
<li>First change node: Set <code>msg.payload</code> to an empty array <code>[]</code>.</li>
<li>Second change node: Set <code>msg.payload</code> to an empty string <code>""</code>.</li>
</ul>
<ol start="28">
<li>
<p>Connect the change nodes to the link-in node to complete the flow.</p>
</li>
<li>
<p>Connect the first change node to the <strong>ui dropdown widget</strong> and the second to the <strong>text input widget</strong>.</p>
</li>
<li>
<p>Deploy the changes.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/submit-request-flow-ggy4A2P9Rc-1848.avif 1848w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/submit-request-flow-ggy4A2P9Rc-1848.webp 1848w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED flow showing the logic for validating and storing user-submitted support requests." loading="lazy" decoding="async" src="https://flowfuse.com/img/submit-request-flow-ggy4A2P9Rc-1848.jpeg" width="1848" height="424" /></picture>
<em>Node-RED flow to handle request submission, including form validation, timestamp formatting, and SQL database insertion.</em></p>
<p>We have now successfully built one part of the Andon task dashboard. You can open the line view in the dashboard and check whether you can submit a request, mark it as acknowledged, and resolve it.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/submit-form-khP624LjYc-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/submit-form-khP624LjYc-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="UI form with dropdown, text input, and a submit button labeled "Request Support."" loading="lazy" decoding="async" src="https://flowfuse.com/img/submit-form-khP624LjYc-1920.jpeg" width="1920" height="1009" /></picture>
<em>Dashboard UI where users select a department, enter notes, and submit a support request for the production line.</em></p>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/building-andon-task-manager-dashboard-with-ff/#up-next"></a> Up Next</h2>
<p>Up until now, the focus has been on building the core functionality of the Andon Task Manager dashboard, including the Lines page. Design and layout have not been the priority. In the next part, you will learn how to enhance the visual design, improve usability, and create a dedicated page and menu for departments.</p>
<p>Later, we will guide you through building the Admin page for the Andon Task Manager dashboard—enabling request management, department configuration, and overall system control.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/What to Measure on the Shop Floor: Factory KPIs Your MES Should DeliverFrom shop floor data to strategic advantage2025-06-20T00:00:00ZSumit Shinde<p>When discussing an <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/">MES</a>, data is always at the core. In previous articles, we explored the crucial steps of collecting and structuring valuable operational data. However, merely having cleaned and structured data is not enough. Many manufacturers find themselves staring at thousands of records while trying to monitor equipment performance or track production, only to feel overwhelmed and unsure of what actions to take. This is not just time-consuming; it often leads to delayed decisions and missed opportunities.</p>
<!--more-->
<p>It is not the volume of data that drives improvement, it is clarity. What manufacturers need is a way to cut through the noise and highlight what truly matters. Factory KPIs (Key Performance Indicators) do exactly that. By focusing attention on the metrics that reflect performance and progress, they turn complexity into clarity and enable faster, more confident decisions.</p>
<p>In this article, we will dive into the most critical factory KPIs that directly impact your bottom line and are a fundamental part of any effective MES implementation. These KPIs empower you to drive profit and eliminate waste.</p>
<h2 id="strategic-kpi-categories-for-mes-success" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#strategic-kpi-categories-for-mes-success"></a> Strategic KPI Categories for MES Success</h2>
<p>There are hundreds of factory KPIs available, but not all are relevant for every MES implementation. To effectively leverage MES and improve factory operations, it is important to organize KPIs into logical categories that support its core objectives. These categories provide a holistic view of factory performance and align operational data with key manufacturing goals.</p>
<p>While many KPIs can be tracked, the most impactful ones in the context of MES typically fall into the following strategic categories:</p>
<ul>
<li><strong>Productivity</strong>: Measuring the efficiency and output of production lines.</li>
<li><strong>Processes</strong>: Assessing how reliably the production chain functions.</li>
<li><strong>Deadlines</strong>: Tracking time-related performance metrics.</li>
<li><strong>Inventory Management</strong>: Ensuring materials and products flow smoothly.</li>
<li><strong>Resources</strong>: Evaluating equipment reliability and effectiveness.</li>
<li><strong>Quality</strong>: Measuring consistency, conformance, and product standards.</li>
</ul>
<h3 id="key-factory-kpis" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#key-factory-kpis"></a> Key Factory KPIs</h3>
<p>Here are some of the most crucial KPIs your <strong>MES</strong> should help you track:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/KPIs-n0Ww-mFio2-956.avif 956w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/KPIs-n0Ww-mFio2-956.webp 956w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="KPIs Tables" loading="lazy" decoding="async" src="https://flowfuse.com/img/KPIs-n0Ww-mFio2-956.jpeg" width="956" height="1474" /></picture>
<em>KPIs Tables</em></p>
<h2 id="turning-your-data-into-kpis" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#turning-your-data-into-kpis"></a> Turning Your Data into KPIs</h2>
<p>Now that you understand the critical factory KPIs, the next logical question is: How does your MES deliver these insights? It’s not about manual calculations or picking up a calculator. The MES should have an integrated pipeline that handles everything—from raw data collection to the clear, actionable KPIs displayed on your dashboards.</p>
<p>Once the data is collected and stored from the factory floor, this pipeline involves four essential stages:</p>
<ul>
<li><strong>Retrieve Data</strong>: Pull the necessary stored data from the central database to perform KPI calculations.</li>
<li><strong>Build Logic</strong>: Define the rules, conditions, and formulas based on operational goals and KPI definitions.</li>
<li><strong>Calculate KPIs</strong>: Execute real-time or scheduled computations to derive metric values from the retrieved data.</li>
<li><strong>Visualize</strong>: Display the results using dashboards to provide clear insights for operators, supervisors, and decision-makers.</li>
</ul>
<p>Once the workflow is set up, it should run automatically in the background—pulling in fresh data, applying the logic, and updating dashboards in real time. This ensures that performance metrics are always current and actionable.</p>
<h3 id="building-kpi-workflow-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#building-kpi-workflow-with-flowfuse"></a> Building KPI Workflow with FlowFuse</h3>
<p>To turn stored factory data into live KPIs, you need a solution with a powerful logic engine capable of processing at scale. Ideally, this solution should also support the rapid development of industrial applications. This is where <strong>FlowFuse</strong>, built on <strong>Node-RED</strong>, offers a clear advantage.</p>
<p>FlowFuse provides a low-code environment that unifies data connectivity, processing logic, and visualization. As covered in our previous articles—<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/">MES Data Acquisition: How to Unlock Your Factory’s Hidden Data</a> and <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/">Structuring and Storing Data for Effective MES Integration</a>—FlowFuse connects seamlessly to nearly all shop floor assets, prepares the data, and stores it in an accessible format.</p>
<p>From there, KPI logic flows can be built visually, defining how and when data is retrieved, how calculations are applied, and how results are displayed. This enables continuous KPI updates without manual effort.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-7F28CZtRfZ-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-7F28CZtRfZ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard built with FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-7F28CZtRfZ-1300.jpeg" width="1300" height="678" /></picture><br />
<em>OEE Dashboard built with FlowFuse</em></p>
<p>The example above shows an <a href="https://flowfuse.com/blueprints/manufacturing/oee-dashboard/">OEE Dashboard</a>, updated every 10 seconds using FlowFuse. Operational data is automatically retrieved, processed, and visualized, delivering accurate, real-time metrics through gauges, charts, and tables.</p>
<p>The following flow powers this dashboard:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-flow-lE9OchBlQv-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-flow-lE9OchBlQv-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard Flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-flow-lE9OchBlQv-1920.jpeg" width="1920" height="1094" /></picture><br />
<em>OEE Dashboard Flow</em></p>
<p>Each node in this flow performs a specific task: retrieving data, transforming it, applying OEE formulas, and delivering results to the dashboard. The entire process is automated and repeatable, providing a real-time view of equipment performance and production health.</p>
<h4 id="build-your-first-kpi-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#build-your-first-kpi-flow"></a> Build Your First KPI Flow</h4>
<p>Let’s see how easy it is to calculate a KPI with a practical example where we will calculate <strong>Machine Downtime</strong>, using demo data stored in an SQLite database.</p>
<h5 id="to-begin" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#to-begin"></a> To Begin</h5>
<ol>
<li>Login to the FlowFuse platform. If you don't have an account, you can register for a <a href="https://app.flowfuse.com/account/create">free trial</a> to get started. Once registered, create a FlowFuse instance and open its editor.</li>
<li>Install the <code>node-red-node-sqlite</code> from the Palette Manager.</li>
<li>For the demo database, import the following database flow. Upon deployment, it will create an SQLite table and insert the demo data:</li>
</ol>
<div id="nr-flow-177" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow177 = "\n[{\"id\":\"2b80c9ff5297fcf0\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":620,\"y\":1540,\"wires\":[[\"0e3ad13c7f083c30\"]]},{\"id\":\"a0a5bf61836aaaba\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"name\":\"debug 6\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1160,\"y\":1540,\"wires\":[]},{\"id\":\"0e3ad13c7f083c30\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"mydb\":\"0710931c9543fc07\",\"sqlquery\":\"fixed\",\"sql\":\"CREATE TABLE machine_runtime_logs (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n machine_id TEXT NOT NULL,\\n date DATE NOT NULL,\\n total_operational_time INTEGER NOT NULL, -- in minutes, e.g., shift duration or planned availability\\n run_time INTEGER NOT NULL -- in minutes, actual machine active time\\n);\\n\",\"name\":\"Create 'machine_runtime_logs' table\",\"x\":910,\"y\":1540,\"wires\":[[\"a0a5bf61836aaaba\"]]},{\"id\":\"d9b8478110727f43\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":620,\"y\":1620,\"wires\":[[\"65e503477a2e5349\"]]},{\"id\":\"65e503477a2e5349\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"mydb\":\"0710931c9543fc07\",\"sqlquery\":\"fixed\",\"sql\":\"INSERT INTO machine_runtime_logs (machine_id, date, total_operational_time, run_time) VALUES\\n('BX02', '2025-06-10', 480, 410),\\n('AX01', '2025-06-10', 480, 465),\\n('BX02', '2025-06-11', 480, 400),\\n('AX01', '2025-06-12', 480, 455),\\n('BX02', '2025-06-12', 480, 390),\\n('AX01', '2025-06-11', 480, 470),\\n('AX01', '2025-06-13', 480, 460),\\n('BX02', '2025-06-13', 480, 420),\\n('AX01', '2025-06-14', 480, 405),\\n('AX01', '2025-06-14', 480, 450);\\n\",\"name\":\"Insert Demo Data\",\"x\":850,\"y\":1620,\"wires\":[[\"45f1950b01ebb3f3\"]]},{\"id\":\"45f1950b01ebb3f3\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"name\":\"debug 7\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1160,\"y\":1620,\"wires\":[]},{\"id\":\"0710931c9543fc07\",\"type\":\"sqlitedb\",\"db\":\"/tmp/sqlite\",\"mode\":\"RWC\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow177.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-177') })</script>
<h5 id="flow-setup" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#flow-setup"></a> Flow Setup</h5>
<ol>
<li>
<p>Drag an <strong>Inject</strong> node onto the canvas and configure it to trigger at your desired interval (e.g., every 10 seconds) to start the data retrieval process.</p>
</li>
<li>
<p>Drag a <strong>Change</strong> node and connect it to the Inject node, adding elements to include query parameters for sql query as shown in the following image.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/params-change-node-hf75KRf3mO-1010.avif 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/params-change-node-hf75KRf3mO-1010.webp 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Change node setting parameters" loading="lazy" decoding="async" src="https://flowfuse.com/img/params-change-node-hf75KRf3mO-1010.jpeg" width="1010" height="1314" /></picture>
<em>Change node setting parameters</em></p>
<ol start="3">
<li>Drag an <strong>SQLite</strong> node, connect it to the Change node, and configure it with following sql statement and select set query to "Prepared Statement"</li>
</ol>
<div style="position: relative" id="code-container-173">
<pre class="language-sql"><code id="code-173" class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span><br /><span class="token keyword">FROM</span> machine_runtime_logs<br /><span class="token keyword">WHERE</span> machine_id <span class="token operator">=</span> $machine_id<br /> <span class="token operator">AND</span> <span class="token keyword">date</span> <span class="token operator">BETWEEN</span> $start_date <span class="token operator">AND</span> $end_date<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-173" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>
<p>Drag a <strong>Split</strong> node and connect it to the SQLite node to break down the returned array or multiple rows from the database.</p>
</li>
<li>
<p>Drag <strong>two Join</strong> nodes and connect them to the output of the Split node.</p>
</li>
<li>
<p>Configure the <strong>first Join</strong> node in <em>Reduce</em> mode to sum <code>total_operational_time</code> using the expression <code>$A + payload.total_operational_time</code>.</p>
</li>
<li>
<p>Configure the <strong>second Join</strong> node in <em>Reduce</em> mode to sum <code>run_time</code> using the expression <code>$A + payload.run_time</code>.</p>
</li>
<li>
<p>Drag a <strong>Change</strong> node and connect it to the first Join node to store <code>msg.payload</code> as <code>flow.total_operational_time</code>.</p>
</li>
<li>
<p>Drag another <strong>Change</strong> node and connect it to the second Join node to store <code>msg.payload</code> as <code>flow.run_time</code>.</p>
</li>
<li>
<p>Drag a <strong>new Change</strong> node and connect both previous Change nodes to it, then set <code>msg.payload</code> using the following JSONata expression to calculate Machine Downtime Percentage:</p>
</li>
</ol>
<pre><code>(($flowContext("total_operational_time") - $flowContext("run_time")) / $flowContext("total_operational_time")) * 100
</code></pre>
<ol start="11">
<li>
<p>Drag a <strong>Debug</strong> node onto the canvas, connect it to the last Change node, and configure it to display <code>msg.payload</code>.</p>
</li>
<li>
<p><strong>Deploy</strong> the flow to continuously calculate and output the Machine Downtime Percentage in real time.</p>
</li>
</ol>
<p>After deploying the flow, observe the debug sidebar on the right. You'll see the calculated machine downtime percentage being continuously printed. To present this data visually, leverage <a href="https://dashboard.flowfuse.com/">Flowfuse Dashboard</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/downtime-A6_UcNl21H-630.avif 630w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/downtime-A6_UcNl21H-630.webp 630w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Downtime printed on debug panel" loading="lazy" decoding="async" src="https://flowfuse.com/img/downtime-A6_UcNl21H-630.jpeg" width="630" height="892" /></picture>
<em>Downtime printed on debug panel</em></p>
<p>You’ve just seen how easy it is to calculate a foundational KPI like machine downtime. But with FlowFuse’s low-code environment, this is just the beginning. You not only connect, collect, transform, and visualize data—but also scale effortlessly.</p>
<p>FlowFuse enables you to manage thousands of device instances remotely, with built-in security, access control, and version management. This level of control ensures consistency across your factory operations and allows you to deploy updates and changes quickly with confidence.</p>
<p>There is much more to explore—FlowFuse gives you the flexibility and power to build a fully integrated, intelligent MES solution that grows with your manufacturing needs.</p>
<p><strong>What does this mean for your factory?</strong></p>
<ul>
<li>You'll put more money in your pocket, by slashing hidden waste and squeezing every drop of efficiency out of your machines and time.</li>
<li>You'll be in total control, ditching the daily firefighting for a clear view that lets you fix problems before they even start.</li>
<li>You'll produce quality you're genuinely proud of, every single time, catching issues on the spot for fewer headaches and happier customers.</li>
<li>You'll unleash the genius of your own team, giving them the insights they need to innovate and drive continuous improvement on the floor.</li>
<li>You'll leap into tomorrow's factory, today, rapidly building powerful solutions that make your entire operation smarter, faster, and truly future-ready.</li>
</ul>
<h2 id="final-thought" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/shop-floor-kpis-for-mes/#final-thought"></a> Final Thought</h2>
<p>We've talked a lot about Factory KPIs and how they're not just some numbers on a screen; they're your secret weapon. And we've seen how a powerful MES, especially one built on something as flexible as FlowFuse, is the real workhorse, taking all that raw data and turning it into clear, actionable gold.</p>
<p>Forget about guessing games. With the right MES and smart KPIs, you get the full picture, right now. It means knowing what is happening on your floor, fixing issues fast, and making decisions that drive measurable impact.</p>
<p><a href="https://flowfuse.com/book-demo/">Schedule a demo</a> and we'll show you how FlowFuse transforms your operations.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/Structuring and Storing Data for Effective MES IntegrationMaking Factory Data Tell the Right Story for Your MES2025-06-18T00:00:00ZSumit Shinde<p>Collecting factory data for your MES is just the first step. If that data isn't properly organized, cleaned, and stored, it's a jumbled mess, leading to missed opportunities and wasted investments. Disorganized information prevents your MES from quickly finding, understanding, and comparing crucial data, directly impacting production, increasing errors, and hindering confident decision-making.</p>
<!--more-->
<p>FlowFuse simplifies <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/">live factory data acquisition</a>, and now it's time to make that data work harder. This article dives into best practices for structuring and storing factory data, helping you maximize your MES's performance and turn raw information into a powerful tool.</p>
<h2 id="core-strategies-for-structuring-your-factory-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#core-strategies-for-structuring-your-factory-data"></a> Core Strategies for Structuring Your Factory Data</h2>
<p>Now that we understand the importance of data structuring, let's explore how to achieve it. This involves giving your data a clear shape and defining rules, ensuring that every system, that is part of your MES, can easily interpret the meaning of each piece of information.</p>
<p>Here are some straightforward ways we get data structred:</p>
<ul>
<li>
<p><strong>Making a Plan for Your Data (Data Modeling):</strong> This is like drawing a simple map for your data. It helps you decide exactly what pieces of information you'll collect (like machine temperature, how many items are made, or who operated the machine) and how they connect to each other. This keeps everything neat and consistent. For example, a data model might say that every "production run" must have a "start time" and an "end time." This makes sure your MES always gets the full picture and avoids confusing or incomplete information.</p>
</li>
<li>
<p><strong>Speaking the Same Language (Standardizing):</strong> Imagine if everyone in your factory used different words for the same thing. It would be confusing! Standardizing means always using the same names, units, and formats everywhere. For example, if you measure temperature, always use Celsius. If one machine sends "TempC" and another just "Temperature," standardizing ensures both are read as "Temperature in Celsius." This prevents your MES from getting confused by different terms for the same data.</p>
</li>
<li>
<p><strong>Adding the Full Story (Contextulization):</strong> A raw number like "100" by itself doesn't tell you much. But if you add "100 items made by Machine A on June 10th at 2:00 PM in Batch 123," suddenly you know the whole story! This means attaching important details like the exact time, the machine's name, the batch number, or who was working at that moment. This extra information makes raw numbers meaningful, so your MES can track things accurately and you can make smarter decisions based on the full picture.</p>
</li>
</ul>
<h2 id="how-flowfuse-brings-your-data-strategy-to-life" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#how-flowfuse-brings-your-data-strategy-to-life"></a> How FlowFuse Brings Your Data Strategy to Life</h2>
<p>FlowFuse simplifies data structuring with its intuitive, drag-and-drop environment. Raw machine data—often just numeric signals—can be enriched, formatted, and organized in real time, <strong>without writing any code</strong>.</p>
<p>You can easily:</p>
<ul>
<li>Add timestamps to readings</li>
<li>Associate data with specific machines or lines</li>
<li>Convert units (e.g., Fahrenheit to Celsius)</li>
<li>Rename fields for consistency</li>
</ul>
<p>FlowFuse comes with standard nodes, like <code>split</code>, <code>change</code>, <code>join</code>, and <code>switch</code>. These let you visually tell FlowFuse how to transform your data. These nodes handle all the technical work for you. You just connect the blocks that clean your data, add context, and prepare it for use in monitoring dashboards or other industrial applications.</p>
<p>There's another simple and powerful node to tell you about. It lets you handle data modeling, standardization, and contextualization, plus it checks your data to make sure it's in the correct range or type. We call this method JSON Schema validation.</p>
<blockquote>
<p><strong>JSON Schema</strong> is a vocabulary that allows you to annotate and validate JSON documents. It defines the structure, data types, and validation rules, ensuring consistency and interoperability across different applications and systems.</p>
</blockquote>
<h3 id="setting-up-json-schema-validation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#setting-up-json-schema-validation"></a> Setting Up JSON Schema Validation</h3>
<p>To get started, open your <strong>FlowFuse instance editor</strong>.</p>
<p>Next, you'll need to install the specific node for JSON Schema validation. Search for and install <code>node-red-contrib-json-full-schema-validator</code>.</p>
<p>Once the node is installed, your next step is to <strong>plan your data schema</strong>. This involves deciding:</p>
<ul>
<li><strong>Which properties are essential</strong> for your data.</li>
<li><strong>What data types</strong> these properties should have (e.g., number, string, boolean).</li>
<li><strong>The units</strong> for numerical data (e.g., if it's temperature, should it be Celsius or Fahrenheit?).</li>
<li><strong>Valid ranges</strong> for your data (e.g., a temperature range of -40°C to 150°C).</li>
<li>Other factors like <strong>precision</strong>, <strong>mandatory fields</strong>, and any <strong>additional attributes</strong>.</li>
</ul>
<p>After planning, you'll prepare this schema in <strong>JSON format</strong>. For a comprehensive guide on how to define your JSON Schemas, check out this helpful: <a href="https://json-schema.org/learn/getting-started-step-by-step">Getting Started Guide</a>.</p>
<div style="position: relative" id="code-container-117">
<pre class="language-json"><code id="code-117" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Hydraulic Pump"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span><br /> <span class="token property">"required"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"timestamp"</span><span class="token punctuation">,</span> <span class="token string">"temperature"</span><span class="token punctuation">,</span> <span class="token string">"pressure"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><span class="token punctuation">,</span><br /> <span class="token property">"format"</span><span class="token operator">:</span> <span class="token string">"date-time"</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Timestamp of when the data was recorded."</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"temperature"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span><br /> <span class="token property">"required"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"value"</span><span class="token punctuation">,</span> <span class="token string">"unit"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The temperature value."</span><span class="token punctuation">,</span><br /> <span class="token property">"minimum"</span><span class="token operator">:</span> <span class="token number">0</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><span class="token punctuation">,</span><br /> <span class="token property">"enum"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Celsius"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The unit of the temperature value."</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"pressure"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span><br /> <span class="token property">"required"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"value"</span><span class="token punctuation">,</span> <span class="token string">"unit"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The pressure value."</span><span class="token punctuation">,</span><br /> <span class="token property">"minimum"</span><span class="token operator">:</span> <span class="token number">0</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><span class="token punctuation">,</span><br /> <span class="token property">"enum"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Pascal"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The unit of the pressure value."</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-117" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This JSON schema defines the structure for data related to a hydraulic pump. It includes three key properties: <code>timestamp</code>, <code>temperature</code>, and <code>pressure</code>. The <code>timestamp</code> must be in a valid date-time format. Both <code>temperature</code> and <code>pressure</code> require two properties: value (a number representing the actual measurement) and <code>unit</code> (which must be Celsius for <code>temperature</code> and Pascal for <code>pressure</code>). Both values must be greater than or equal to zero. This schema ensures that all data is recorded with the correct units and valid values, maintaining consistency and reliability.</p>
<h3 id="implementing-data-schema-validation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#implementing-data-schema-validation"></a> Implementing Data Schema Validation</h3>
<p>Let's implement the data schema validation mechanism to ensure each incoming data adheres to the specified JSON schema.</p>
<ol>
<li>Drag the <strong>JSON Full Schema Validator</strong> node onto the Node-RED canvas.</li>
<li>Double-click the node to open its settings.</li>
<li>Copy and paste your schema into the node’s schema field.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/json-full-validator-node-MaYbl-4QS3-858.avif 858w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/json-full-validator-node-MaYbl-4QS3-858.webp 858w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring "JSON Full Schema Validator" node with JSON schema for our data " loading="lazy" decoding="async" src="https://flowfuse.com/img/json-full-validator-node-MaYbl-4QS3-858.jpeg" width="858" height="1560" /></picture>
<em>Configuring "JSON Full Schema Validator" node with JSON schema for our data</em></p>
<ol start="4">
<li>Click <strong>Done</strong> to save the changes.</li>
<li>Connect the input of the <strong>JSON Full Schema Validator</strong> node to the data source from where your data is coming.</li>
<li>Connect the node's first output to another node to process or handle the validated data (e.g., an MQTT node, a database node, or any other destination).</li>
<li>Connect the second output to the flow that will handle the situation where data does not meet the schema. This could be a notification flow sending an email or Telegram to your team or a dashboard alert.</li>
<li>Deploy the flow.</li>
</ol>
<p>Now, let's understand this with an example. Below is the data that we are receiving from the PLC. After transforming it, we’ve added essential properties such as unit and value. However, notice that the data doesn't meet the schema definition because the temperature is given in Fahrenheit and is a negative number, which isn't within the expected range.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/invalid-data-0t_J9bTwO4-1080.avif 1080w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/invalid-data-0t_J9bTwO4-1080.webp 1080w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Message passes through the second output and includes errors when it does not align with the data schema." loading="lazy" decoding="async" src="https://flowfuse.com/img/invalid-data-0t_J9bTwO4-1080.jpeg" width="1080" height="572" /></picture>
<em>Message passes through the second output and includes errors when it does not align with the data schema.</em></p>
<p>If the data doesn't align with the data schema, it will pass through the "JSON Full Schema Validator" node and flow through the second output. The message will contain an error array with detailed information about what is wrong with the data (e.g., incorrect unit or out-of-range value). From this output, you can easily connect email or Telegram nodes to send alert notifications.</p>
<p>When the data meets the schema, it passes through the first output without errors. The validated data is then sent to the next stage in the flow.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/valid-data-message-7bppLBOiWh-1080.avif 1080w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/valid-data-message-7bppLBOiWh-1080.webp 1080w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Message passes through the first output and does not include errors when it aligns with the data schema." loading="lazy" decoding="async" src="https://flowfuse.com/img/valid-data-message-7bppLBOiWh-1080.jpeg" width="1080" height="341" /></picture>
<em>Message passes through the first output and does not include errors when it aligns with the data schema.</em></p>
<h2 id="where-to-keep-all-that-factory-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#where-to-keep-all-that-factory-data"></a> Where to Keep All That Factory Data</h2>
<p>Once your factory data is structured and validated, you need a smart place to store it. Different kinds of information often require different storage types to be most useful for your MES.</p>
<p>Here are the main types of storage typically used for factory data:</p>
<ul>
<li>
<p><strong>Time-Series Databases (TSDBs):</strong> Perfect for constantly changing data like sensor readings (temperature, machine speed). They handle massive updates efficiently, ideal for spotting trends over time. Think of them as a super-efficient diary recording every moment. <strong>InfluxDB</strong> and <strong>TimescaleDB</strong> are good examples.</p>
</li>
<li>
<p><strong>Standard Databases (SQL Databases):</strong> Best for structured information with clear connections, such as production orders, material usage per batch, or quality check results. They keep data organized and ensure correct links between pieces of information, like a well-organized spreadsheet. You'll often see <strong>PostgreSQL</strong> or <strong>MySQL</strong> used here.</p>
</li>
<li>
<p><strong>Data Lakes or Cloud Storage:</strong> Use these for vast amounts of diverse data, even if it's not perfectly organized. They're great for long-term historical records or data you'll analyze later with advanced tools. Imagine them as a huge warehouse for anything, ready when you need to sort through it. <strong>Amazon S3</strong> and <strong>Azure Data Lake Storage</strong> are common examples.</p>
</li>
</ul>
<p>When choosing storage, consider your data volume and speed, how often you need to access it (real-time vs. historical), cost, scalability, system connectivity, and data security.</p>
<h2 id="flowfuse-also-helps-with-data-routing-to-storage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#flowfuse-also-helps-with-data-routing-to-storage"></a> FlowFuse Also Helps with Data Routing to Storage</h2>
<p>FlowFuse isn't just for changing and cleaning data; it also makes sure your data gets to the right storage spot.</p>
<p>It has tons of nodes for almost every database and cloud storage system you'll find in a factory today.</p>
<p>This includes direct <strong>connections</strong> to normal databases like <a href="https://flows.nodered.org/node/node-red-node-mysql">MySQL</a> and <a href="https://flows.nodered.org/node/node-red-contrib-postgresql">PostgreSQL</a> for your organized production data.</p>
<p>It also has special nodes for time-series databases like <a href="https://flows.nodered.org/node/node-red-contrib-influxdb">InfluxDB</a> and <a href="https://flowfuse.com/node-red/database/timescaledb/">TimescaleDB</a> to handle fast sensor and machine data.</p>
<p>Plus, FlowFuse <strong>connects</strong> to big Data Lakes and Cloud Storage services like <a href="https://flows.nodered.org/node/node-red-node-aws">Amazon S3</a>, <a href="https://flows.nodered.org/node/node-red-contrib-google-cloud">Google Cloud Storage</a>, and <a href="https://flows.nodered.org/node/node-red-contrib-azure-storage">Microsoft Azure</a>.</p>
<p><strong>FlowFuse Fuels Your MES Dashboards and Applications</strong></p>
<p>Once your factory data is effectively structured, validated, and routed by FlowFuse to the right place, it becomes an incredibly powerful asset for your MES.</p>
<p>FlowFuse doesn't just manage your data; it also empowers you to easily build the user interface (UI) for your MES. You can create insightful dashboards and industrial applications using a no-code, drag-and-drop approach with components from <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a>. This allows you to design the screens operators use to visualize critical information and control processes, all built upon the reliable data foundation you've established.</p>
<h2 id="final-thought" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/structuring-storing-data-mes-integration/#final-thought"></a> Final Thought</h2>
<p>Your factory's success hinges on smart decisions, and smart decisions need good data. It's not enough to just collect information from your machines; you need to make sense of it.</p>
<p>That means organizing your data so it's clear and consistent. Think of it like putting all your tools in the right place – easy to find when you need them. You also need to clean up the data, getting rid of errors so you can trust what you see. Finally, you need to store it smartly, choosing the best spot for different types of information so it's always ready for use.</p>
<p>FlowFuse helps with all of this. It's like your data's personal assistant, collecting raw information, tidying it up, and sending it to the right storage, all without complicated coding. This ensures your Manufacturing Execution System (MES) gets the accurate, reliable data it needs to help you run your factory smoother and make better choices. Hundreds of other manufacturing companies are already using FlowFuse to transform their data into a powerful asset.</p>
<p>If you want to see FlowFuse in action, <a href="https://flowfuse.com/book-demo/">book a demo</a> today!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/MES Data Acquisition: How to Unlock Your Factory’s Hidden DataBreaking Down Data Silos to Empower Your MES2025-06-13T00:00:00ZSumit Shinde<p>A Manufacturing Execution System (MES) is the central control system of a factory. To work properly, it needs a steady stream of real-time data from machines and systems on the factory floor. This data is essential for running operations smoothly. But in most factories, the hardest part is getting this data to the MES.</p>
<!--more-->
<p>This article dives into the data that fuels your MES and the complex web of sources it comes from. We'll explore the core challenge that keeps this data locked away in silos. Most importantly, we’ll show how <strong>FlowFuse</strong> acts as the catalyst to liberate this data, empowering you to get the right information to your MES, exactly when and where you need it most.</p>
<h2 id="the-operational-data-your-mes-needs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/#the-operational-data-your-mes-needs"></a> The Operational Data Your MES Needs</h2>
<p>For a factory's main computer system (the MES) to do its job, it needs a constant stream of information. Think of it like a control room that needs to see everything at once. This information can be grouped into four main types:</p>
<ul>
<li>
<p><strong>Production Information:</strong> This tells the system how much is being made, how fast it's being made, and if any products had to be thrown out. It's the basic "are we winning?" data.</p>
</li>
<li>
<p><strong>Machine Information:</strong> This is like a live health report for your equipment. It tells the system if a machine is running or stopped, what its settings are (like temperature or speed), and how much power it's using.</p>
</li>
<li>
<p><strong>Context and Quality Information:</strong> This data adds the "why." It tells the system who is working, why a machine stopped, and if the products being made are good enough to sell.</p>
</li>
<li>
<p><strong>Material Information:</strong> This tells the full story of a product. It tracks all the raw materials and parts from start to finish, so you know exactly what went into every single item.</p>
</li>
</ul>
<p>Once the system gets all this information, it does more than just track numbers. It becomes the factory's control center. It gives workers step-by-step instructions on their screens, makes sure rules are followed to prevent mistakes, and lets managers see exactly what’s happening everywhere so they can fix small problems before they become big ones.</p>
<h2 id="the-origins-of-your-operational-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/#the-origins-of-your-operational-data"></a> The Origins Of Your Operational Data</h2>
<p>This critical operational data doesn't live in one place; it's generated across a diverse and complex digital ecosystem.</p>
<p>A vast amount comes directly from shop floor equipment—the PLCs that orchestrate your machines, the thousands of sensors measuring every variable, and the Historians that diligently archive past performance. Then you have your core business systems. The ERP provides the what and why through production orders, while Quality (QCS) and Maintenance (CMMS) systems add essential layers of inspection and machine health data.</p>
<p>Each of these sources speaks its own digital language. A single factory floor is a cacophony of <code>Modbus</code>, <code>OPC UA</code>, <code>EtherNet/IP</code>, and <code>MQTT</code>, etc all running simultaneously. This mix of protocols defines the communication architecture of the operation.</p>
<p>To better understand the data involved, its sources, and the common protocols used, let's look at a detailed breakdown:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-data-landscapes-YLJK7yd7Bx-1008.avif 1008w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-data-landscapes-YLJK7yd7Bx-1008.webp 1008w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A table showing the four main categories of data MES need" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-data-landscapes-YLJK7yd7Bx-1008.jpeg" width="1008" height="828" /></picture>
<em>A table showing the four main categories of data MES need</em></p>
<h2 id="why-it's-so-difficult%2C-slow%2C-and-costly-to-access-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/#why-it's-so-difficult%2C-slow%2C-and-costly-to-access-data"></a> Why It's So Difficult, Slow, and Costly to Access Data</h2>
<p>You know what data you need and where it is. The fundamental question is: can you actually get it from your machines and deliver it to your MES?</p>
<p>This is the central struggle where real-time decisions get delayed, opportunities are lost, and innovation is stifled. Your most valuable data is trapped. Because your factory’s systems don’t speak the same digital language, data is locked away in "silos," inaccessible and unusable. This isn't a technical inconvenience; it's a critical business problem.</p>
<p>Every new piece of equipment demands expensive, custom-coded integrations that are fragile and brittle. This necessitates a constant reliance on specialized programmers, driving up operational costs significantly. The result is a chaotic patchwork of inconsistent data flows, leaving you with a fragmented view of your operation instead of the unified intelligence you desperately need to make swift, informed decisions. This isn't just inefficient; it actively sabotages your agility, cripples your ability to innovate, and fundamentally undermines your competitive advantage.</p>
<h2 id="orchestrate-your-factory's-data-flow-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/#orchestrate-your-factory's-data-flow-with-flowfuse"></a> Orchestrate Your Factory's Data Flow with FlowFuse</h2>
<p>It's frustrating to know the data is there but be unable to reach it. FlowFuse was built to solve this exact problem by acting as a data acquisition layer for your factory. It creates reliable pathways for information to get from your various machines and systems directly to your MES.</p>
<p>The power of FlowFuse lies in its foundation on the vast Node-RED ecosystem. This gives you immediate access to a library of over 5,000 pre-built connectors, or "nodes" ready to communicate with a massive array of industrial protocols. This eliminates the need for expensive, time-consuming custom code. The library includes robust nodes for standards like Modbus, OPC UA, and MQTT, as well as for specific controllers from Siemens, Mitsubishi, Omron, and more.</p>
<p>Following are some of the most commonly used protocol nodes:</p>
<ul>
<li><strong>Modbus:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-modbus">https://flows.nodered.org/node/node-red-contrib-modbus</a></li>
<li><strong>OPC UA:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-opcua">https://flows.nodered.org/node/node-red-contrib-opcua</a></li>
<li><strong>OPC DA:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-opc-da">https://flows.nodered.org/node/node-red-contrib-opc-da</a></li>
<li><strong>MQTT:</strong> <a href="https://flowfuse.com/node-red/core-nodes/mqtt/">https://flowfuse.com/node-red/core-nodes/mqtt/</a></li>
<li><strong>Ethernet/IP:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-ethernet-ip">https://flows.nodered.org/node/node-red-contrib-ethernet-ip</a></li>
<li><strong>Siemens S7:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-s7comm">https://flows.nodered.org/node/node-red-contrib-s7comm</a></li>
<li><strong>MITSUBISHI MC:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-mcprotocol">https://flows.nodered.org/node/node-red-contrib-mcprotocol</a></li>
<li><strong>OMRON FINS:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-omron-fins">https://flows.nodered.org/node/node-red-contrib-omron-fins</a></li>
<li><strong>HTTP:</strong> <a href="https://flowfuse.com/node-red/core-nodes/http-in/">https://flowfuse.com/node-red/core-nodes/http-in/</a></li>
<li><strong>LwM2M:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-lwm2m">https://flows.nodered.org/node/node-red-contrib-lwm2m</a></li>
<li><strong>AMQP:</strong> <a href="https://flowfuse.com/node-red/protocol/amqp/">https://flowfuse.com/node-red/protocol/amqp/</a></li>
<li><strong>Serialport:</strong> <a href="https://flows.nodered.org/node/node-red-node-serialport">https://flows.nodered.org/node/node-red-node-serialport</a></li>
<li><strong>GPIO:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-gpio">https://flows.nodered.org/node/node-red-contrib-gpio</a></li>
<li><strong>Lorawan:</strong> <a href="https://flows.nodered.org/node/node-red-contrib-lorawan">https://flows.nodered.org/node/node-red-contrib-lorawan</a></li>
</ul>
<p>This extensive library allows you to reliably acquire data from various assets using a simple drag-and-drop approach, bringing your factory's siloed data into a cohesive and manageable flow.</p>
<p>In addition to protocol connectors, there are also powerful database nodes available to integrate with systems such as InfluxDB, TimescaleDB, PostgreSQL, Microsoft SQL Server, and more—making it easy to store, query, and analyze your factory data.</p>
<p>So, With FlowFuse, you can:</p>
<ul>
<li><strong>Deploy intelligent agents</strong> directly to the edge, all managed from a central platform remotely.</li>
<li><strong>Connect to any industrial asset</strong>—PLCs, sensors, SCADA—using ready-made nodes.</li>
<li><strong>Transform raw data</strong> with visual logic, so it’s perfectly structured for your MES.</li>
<li><strong>Build custom operator dashboards</strong> with pre-built UI widgets to visualize and act on data.</li>
<li><strong>Automate data flows</strong> based on schedules, machine events, or production states.</li>
<li><strong>Secure the entire process</strong> with enterprise-grade features like multi-user authentication and role-based access control.</li>
<li><strong>Scale seamlessly</strong> from a single line to your entire enterprise.</li>
</ul>
<p>This isn't just about solving a technical challenge. It’s about driving business outcomes. When you can finally see your entire operation in one clear picture, your production lines run more efficiently. You'll see tangible savings as you reduce waste and catch errors before they become costly. When you are known for exceptional quality and effortless compliance, you win. You can turn the messy, trapped data that has been holding you back into the very asset that pushes you ahead of the competition.</p>
<h2 id="your-next-step-towards-operational-excellence" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/data-acquisition-for-mes/#your-next-step-towards-operational-excellence"></a> Your Next Step Towards Operational Excellence</h2>
<p>Bridging the gap between your factory floor and your MES is a huge task. The sheer diversity of machines, systems, and protocols can seem impossible to overcome. But it doesn’t have to be a barrier to innovation.</p>
<p>This is precisely where FlowFuse shines. It acts as the universal translator, bringing all your systems together regardless of the language they speak. With thousands of ready-to-use connectors and an intuitive low-code interface, FlowFuse empowers you to get your data flowing exactly where it needs to go.</p>
<p>Once that live data starts moving, your MES becomes exponentially more powerful—helping you spot problems faster, plan smarter, and run your operations with confidence.</p>
<p>Want to see it in action? <a href="https://flowfuse.com/book-demo/">Book a live demo</a> and watch FlowFuse unlock your factory data—no custom code required.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/announcing-node-red-con-2025/Announcing Node-RED Con 2025: A Community Conference on Industrial ApplicationsWe're excited to support this year's community conference focused on Node-RED in industry. The Call for Papers is now open!2025-06-12T00:00:00Z<p>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 <strong>Node-RED Con 2025</strong>, a free, online conference taking place on <strong>Tuesday, November 4, 2025!</strong></p>
<!--more-->
<p>This year's event is dedicated to exploring a vital theme: <strong>Node-RED applications in industry</strong>. 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.</p>
<h2 id="the-call-for-papers-is-now-open" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/announcing-node-red-con-2025/#the-call-for-papers-is-now-open"></a> The Call for Papers is Now Open</h2>
<p>The conference agenda is built from community submissions, and the official Call for Papers is now live. This is a chance to contribute your expertise and share your story with the wider Node-RED ecosystem.</p>
<p>The event is seeking submissions from passionate individuals in several categories:</p>
<ul>
<li><strong>Full Talks & Demos (25-30 mins):</strong> Focused on industrial use cases, IoT architectures, and edge computing.</li>
<li><strong>Lightning Talks (8-10 mins):</strong> Showcasing fun, creative, or inspiring projects.</li>
<li><strong>Expert Panelists:</strong> To discuss the future of industrial automation with Node-RED.</li>
</ul>
<p>The deadline for submissions is <strong>July 25, 2025</strong>.</p>
<p><a href="https://nrcon.nodered.org/">Learn More & Submit Your Proposal</a></p>
<h2 id="would-you-like-to-attend%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/announcing-node-red-con-2025/#would-you-like-to-attend%3F"></a> Would you like to attend?</h2>
<p>For a direct email reminder when registration opens, you can sign up for updates below.</p>
<script charset="utf-8" type="text/javascript" src="https://js-eu1.hsforms.net/forms/embed/v2.js"></script>
<script>
hbspt.forms.create({
portalId: "26586079",
formId: "ec3e2601-7df4-4b8f-89fc-5244da3db732",
region: "eu1"
});
</script>
<p>We are proud to support this fantastic community-led event and can't wait to see the innovations that will be shared. See you in November!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/optimizing-operations-improve-industrial-operations-with-flowfuse/Optimizing operations: Improve Industrial Operations with FlowFuseFlowFuse raises $7.2M to empower you to connect the physical and digital worlds of industry.2025-06-10T00:00:00ZZJ van de Weg<p>The announcement of our latest $7.25 million funding round isn't just a milestone for FlowFuse; it's a catalyst for our mission to revolutionize how industrial data is accessed and used.</p>
<!--more-->
<p>Thousands of IT professionals and citizen developers now collaborate with our product on their digital transformation, and we’re expecting hundreds of thousands more to join over the next few years. We're incredibly proud of our recent growth – nearly 5x in customers and revenue just last year, a trend that’s only accelerating. But more than that, we're thrilled by what this allows us to build for you.</p>
<p>Our focus has always been to empower engineers and operational managers to connect the physical shop floor with their digital enterprise systems. We see FlowFuse, powered by Node-RED, as that critical layer that makes data accessible, understandable, and actionable. And with this new investment, we're doubling down on making this experience even more powerful and intuitive.</p>
<h2 id="flowfuse%3A-the-low-code-powerhouse-for-industrial-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/optimizing-operations-improve-industrial-operations-with-flowfuse/#flowfuse%3A-the-low-code-powerhouse-for-industrial-data"></a> FlowFuse: The Low-Code Powerhouse for Industrial Data</h2>
<p>FlowFuse is enabling customers to bridge the gap between their physical and digital operations. They are leveraging FlowFuse to extract the data required to implement new use-cases from a wide range of sources. FlowFuse spans both digital systems like ERPs and physical equipment on the shop floor. Using the low-code interface of Node-RED within the FlowFuse platform, this captured data is then transformed and efficiently transported to various destinations. Significantly, users are orchestrating multiple Node-RED instances through FlowFuse to collaboratively inform and automate operational decisions, even incorporating crucial operator feedback directly into the workflow.</p>
<p>FlowFuse creates a singular and unified platform for users that not only extracts and transports data but also actively utilizes it for decision-making and incorporates human expertise, effectively closing the loop through fusing your operations with the corporate systems. The void often filled by complex SCADA systems and disparate, siloed solutions can now be seamlessly integrated and managed with FlowFuse.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/low-code-powerhouse-for-industrial-data-A5JtG5Llws-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/low-code-powerhouse-for-industrial-data-A5JtG5Llws-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse, the low-code powerhouse for inustrial data" loading="lazy" decoding="async" src="https://flowfuse.com/img/low-code-powerhouse-for-industrial-data-A5JtG5Llws-1920.jpeg" width="1920" height="748" /></picture></p>
<h2 id="where-we're-going%3A-infusing-ai-for-an-even-smarter-tomorrow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/optimizing-operations-improve-industrial-operations-with-flowfuse/#where-we're-going%3A-infusing-ai-for-an-even-smarter-tomorrow"></a> Where We're Going: Infusing AI for an Even Smarter Tomorrow</h2>
<p>Over the past years we’ve been focused on augmenting Node-RED to enable it to scale better for an organization. This entails many facets:</p>
<ul>
<li>Ensure companies that are in regulated industries meet their compliance frameworks.</li>
<li>Make it easy to run and maintain hundreds or thousands of Node-REDs.</li>
<li>Extend DevOps to operational technology teams, ensure development is done outside of production systems.</li>
</ul>
<p>Our shift towards becoming an <em><strong>end to end</strong></em> platform to connect the physical world with your digital realm is next, and we’ve already started this journey. Over the past months we’ve enabled customers to store data long term by allowing them to store files on the platform. Also, we noticed that customers took longer than ideal to transport data between data consumers and producers and back again. Building a data orchestration layer, for example through a Unified Namespace (UNS), is vital to ensure data is available for building applications. So FlowFuse has allowed thousands of clients to send data through MQTT, a quick on-ramp to further the abilities for the platform of FlowFuse. Lastly, in the next FlowFuse release a data storage layer will be provided to persist and allow querying of events.</p>
<p>While we'll continue to enhance the core capabilities users already love, a significant portion of this new investment is earmarked for integrating AI more deeply into the FlowFuse platform. The FlowFuse AI assistant already has proven itself countless times. However, we believe improving our AI suggestions and capabilities are the key to unlocking the next level of low-code development.</p>
<p>While Node-RED is low-code and enables many engineers the power to integrate, automate, and interact with their operations, we want every engineer – mechanical, electrical, or operational – to harness its full potential quickly. AI will act as an intelligent guide, helping new users understand Node-RED's capabilities, learn best practices, and troubleshoot effectively. Imagine an assistant that helps you build your first flow, suggests optimal configurations, or explains complex functions in simple terms.</p>
<p>Furthermore, we see that AI can offer suggestions, identify potential optimizations, and even help generate partial flows in Node-RED. Allowing both new and experienced users to gain momentum.
FlowFuse is on a mission to get a billion people automating, having AI make suggestions allows more people to learn how, and get started, as well as advanced users remove boilerplate to create and get to brass tacks faster.</p>
<h2 id="the-vision%3A-everyone-can-increase-operational-excellence" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/optimizing-operations-improve-industrial-operations-with-flowfuse/#the-vision%3A-everyone-can-increase-operational-excellence"></a> The Vision: Everyone Can Increase Operational Excellence</h2>
<p>There are millions of brilliant engineers and managers out there who understand their operations inside and out. They know what needs to be optimized, and have hunches to validate, but often lack the accessible tools to bridge the data gap. That’s what FlowFuse is for.</p>
<p>We are committed to making it super easy to get data, derive insight, and act on that insight to optimize operations. The infusion of AI into our low-code platform is the next logical step in this journey. We're incredibly excited about the path ahead and grateful to our customers, partners, and investors for their support. The future of industrial operations is intelligent, connected, and user-empowered – and we're building it, together.</p>
<p>Want to see how FlowFuse can help you optimize your operations? Let's talk about your specific needs and see our platform in action. <a href="https://flowfuse.com/book-demo/">Schedule a demo</a> with our team today.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/FlowFuse Forms: Easy Data Collection for Your Factory FloorGet Shop Floor Data Directly to Machine Settings2025-06-10T00:00:00ZSumit Shinde<p>It's often a pain to get important data from the factory floor. Things like doing quality checks still rely on old methods like manual notes and slow spreadsheets. This can lead to delays, errors, and a lot of wasted time before anyone can actually use the information. It's especially tough when you need quick feedback from an operator.</p>
<!--more-->
<p>This article will show you an easy way to gather data via a form entry. We'll look at how forms in FlowFuse Dashboard can make collecting data from factory workers much simpler. You'll learn how to build useful forms that connects your team's knowledge directly to your industrial processes. As a practical example, we'll walk you through building a solution to digitize production recipe updates, showing you exactly how to implement it.</p>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#prerequisites"></a> Prerequisites</h2>
<p>Before you begin, make sure you have the following:</p>
<ul>
<li><strong>Node-RED:</strong> Make sure you have an instance of Node-RED up and running. The quickest way to do this is via FlowFuse. If you don't have an account, check out our <a href="https://app.flowfuse.com/account/create">free trial</a>.</li>
</ul>
<p>Then you'll need to add two more sets of nodes to your palette:</p>
<ul>
<li><strong>FlowFuse Dashboard:</strong> Ensure you have <a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">FlowFuse Dashboard</a> (also known as Node-RED Dashboard 2.0 in the community) installed and properly configured on your instance.</li>
<li><strong>SQLite:</strong> Install the <a href="https://flows.nodered.org/node/node-red-node-sqlite">node-red-node-sqlite</a> package, which will be used in the practical example.</li>
</ul>
<p>and finally:</p>
<ul>
<li><strong>Basic Node-RED Knowledge:</strong> You are familiar with creating and deploying basic flows in Node-RED. If not, consider taking the <a href="https://node-red-academy.learnworlds.com/course/node-red-getting-started">Node-RED Fundamentals Course</a> <em>sponsored by FlowFuse.</em></li>
</ul>
<h2 id="building-forms-in-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#building-forms-in-flowfuse-dashboard"></a> Building Forms in FlowFuse Dashboard</h2>
<p>The FlowFuse Dashboard makes it easy to build interactive industrial applications using drag-and-drop components — <strong>no coding required</strong>.</p>
<p>One of these components is the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-form.html">Form</a> widget, which allows you to create versatile forms within your applications. The Form widget supports a wide range of input types, including:</p>
<ul>
<li>Text Fields</li>
<li>Number Inputs</li>
<li>Date Pickers,</li>
<li>Multi-line Text Areas</li>
<li>Dropdown Selection</li>
<li>Checkboxes</li>
</ul>
<p>A key benefit of this widget is that you can configure the form fields either statically (with predefined values) or dynamically (updated through your Node-RED flow), depending on your application’s needs.</p>
<h3 id="adding-and-configuring-the-form-widget" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#adding-and-configuring-the-form-widget"></a> Adding and Configuring the Form Widget</h3>
<ol>
<li>Drag the <strong>Form</strong> widget onto the canvas.</li>
<li>Double-click on the widget and create a new group for it with the correct page configuration to render it. (note: if this is the first widget you have, this will automatically be created for you)</li>
<li>Set an appropriate size (width and height) according to your preferences.</li>
<li>Enter the label for the form.</li>
</ol>
<p>Now that we have completed all the basic and necessary configurations for the form, let’s add the input elements.</p>
<h3 id="adding-input-fields-to-the-form-statically" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#adding-input-fields-to-the-form-statically"></a> Adding Input Fields to the Form Statically</h3>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/adding-form-element-VI3og-2z4p-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Adding Form Elements" loading="lazy" decoding="async" src="https://flowfuse.com/img/adding-form-element-VI3og-2z4p-800.webp" width="800" height="726" /></picture>
<em>Adding Form Elements</em></p>
<p>The widget supports various input element types that can be tailored to specific use cases — from collecting simple text to selecting dates or choosing from predefined options.</p>
<p><strong>To add input elements:</strong></p>
<ol>
<li>Click the <strong>+ add</strong> button in the widget’s configuration dialog.</li>
<li>A new configuration row will appear for the element.</li>
<li>Configure each input element with the following fields:</li>
</ol>
<ul>
<li><strong>Label</strong>: This is the visible label for the field shown to the user.</li>
<li><strong>Name</strong>: A unique key used in the message payload (e.g., <code>msg.payload.firstname</code>) when the form is submitted.</li>
<li><strong>Type</strong>: Select the input type. Supported types include:
<ul>
<li><strong>Text</strong>: For short text inputs (e.g., name, city).</li>
<li><strong>Number</strong>: For numeric inputs (e.g., age, price).</li>
<li><strong>Date</strong>: For selecting a date.</li>
<li><strong>Text Area</strong>: For longer free-form text.</li>
<li><strong>Dropdown</strong>: For selecting from a list of predefined values.<br />
<em>We will cover how to add options to the dropdown field in a later section.</em></li>
<li><strong>Checkbox</strong>: For boolean values (checked or unchecked).</li>
</ul>
</li>
<li><strong>Required</strong>: Check this box to make the field mandatory. The form cannot be submitted unless this field is filled.</li>
<li><strong>Row</strong>:: If Multiline is selected, this defines the number of visible rows in the text area.</li>
</ul>
<h3 id="adding-options-to-dropdown-inputs-statically" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#adding-options-to-dropdown-inputs-statically"></a> Adding Options to Dropdown Inputs Statically</h3>
<p>When you add Dropdown type input element to the Form widget, you need to provide a list of <code>options</code> that the user can choose from. These options can be configured in the widget's configuration dialog.</p>
<ol>
<li>In the widget's configuration dialog, switch to the "Dropdown Options" tab.</li>
<li>Click the <strong>+ add</strong> button to insert a new option row.</li>
<li>In the new row, fill in the following fields:
<ul>
<li><strong>Dropdown</strong>: Select the dropdown input field you want to add options to.</li>
<li><strong>Value</strong>: The internal value that will be sent in the form payload when this option is selected.</li>
<li><strong>Label</strong>: The visible text shown to the user in the dropdown list.</li>
</ul>
</li>
</ol>
<p>Repeat this process for each option you want to add.</p>
<h3 id="pre-filling-forms-with-default-values" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#pre-filling-forms-with-default-values"></a> Pre-filling Forms with Default Values</h3>
<p>You can pre-fill forms with default values to streamline user input, reduce typing errors, and save time. This is especially useful in scenarios like editing an existing recipe, where the current details can be loaded directly into the form.</p>
<p>We can pass data to the <code>ui-form</code> node in our flow to set these values dynamically. To do this, send an object in <code>msg.payload</code> to the input of the relevant node. Each key of <code>msg.payload</code> corresponds to a form field and its value represents the pre-filled data.</p>
<p>For example, if your form includes fields for <code>product_name</code> and <code>target_temperature_c</code>, you can send a <code>msg.payload</code> like this:</p>
<div style="position: relative" id="code-container-260">
<pre class="language-javascript"><code id="code-260" class="language-javascript"><span class="token punctuation">{</span><br /> <span class="token string-property property">"product_name"</span><span class="token operator">:</span> <span class="token string">"Eco-Friendly Coating"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"target_temperature_c"</span><span class="token operator">:</span> <span class="token number">120.0</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-260" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="add-form-input-elements-dynamically-at-runtime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#add-form-input-elements-dynamically-at-runtime"></a> Add Form Input Elements Dynamically at Runtime</h3>
<p>In some cases, you may need to define form elements dynamically based on real-time data.</p>
<p>For example, you might want to show additional fields based on a user’s selection or load dropdown options from an external API. This dynamic capability adds a new level of flexibility and interactivity to your forms.</p>
<p><strong>To define form fields at runtime:</strong></p>
<ol>
<li>Use the <code>msg.ui_update.options</code>.</li>
<li><code>options</code> should contain an array of objects, where each object defines the new configuration for the element:</li>
</ol>
<p>Below are the supported element types and their corresponding JSON configurations:</p>
<table>
<thead>
<tr>
<th><strong>Element Type</strong></th>
<th><strong>JSON Configuration</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Text</strong></td>
<td><code>{ "type": "text", "label": "Name", "key": "name", "required": true }</code></td>
</tr>
<tr>
<td><strong>Multiline</strong></td>
<td><code>{ "type": "multiline", "label": "Name", "key": "name", "required": true, "rows": 4 } </code></td>
</tr>
<tr>
<td><strong>Password</strong></td>
<td><code>{ "type": "password", "label": "Password", "key": "password", "required": true } </code></td>
</tr>
<tr>
<td><strong>Email</strong></td>
<td><code>{ "type": "email", "label": "E-Mail Address", "key": "email", "required": true } </code></td>
</tr>
<tr>
<td><strong>Number</strong></td>
<td><code>{ "type": "number", "label": "Age", "key": "age", "required": true }</code></td>
</tr>
<tr>
<td><strong>Checkbox</strong></td>
<td><code>{ "type": "checkbox", "label": "Subscribe to Newsletter", "key": "newsletter" } </code></td>
</tr>
<tr>
<td><strong>Switch</strong></td>
<td><code>{ "type": "switch", "label": "Enable Notifications", "key": "notifications" }</code></td>
</tr>
<tr>
<td><strong>Date</strong></td>
<td><code>{ "type": "date", "label": "Date of Birth", "key": "dob", "required": true } </code></td>
</tr>
<tr>
<td><strong>Time</strong></td>
<td><code>{ "type": "time", "label": "Time of Birth", "key": "tob", "required": true } </code></td>
</tr>
<tr>
<td><strong>Dropdown</strong></td>
<td><code>{ "type": "dropdown", "label": "Dropdown", "key": "selection" } </code></td>
</tr>
</tbody>
</table>
<h3 id="adding-options-to-dropdown-inputs-dynamically-at-runtime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#adding-options-to-dropdown-inputs-dynamically-at-runtime"></a> Adding Options to Dropdown Inputs Dynamically at Runtime</h3>
<p>To update the options of a dropdown field at runtime, use the <code>msg.ui_update.dropdownOptions</code> property in your flow.</p>
<p>This is useful when you want to update just the dropdown options without changing the rest of the form.</p>
<p><strong>Example:</strong></p>
<div style="position: relative" id="code-container-394">
<pre class="language-json"><code id="code-394" class="language-json"><span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"dropdown"</span><span class="token operator">:</span> <span class="token string">"Machine Type"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"A"</span><span class="token punctuation">,</span><br /> <span class="token property">"label"</span><span class="token operator">:</span> <span class="token string">"Option A"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"dropdown"</span><span class="token operator">:</span> <span class="token string">"Machine Type"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token string">"B"</span><span class="token punctuation">,</span><br /> <span class="token property">"label"</span><span class="token operator">:</span> <span class="token string">"Option B"</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-394" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The "dropdown" refers to the name of the dropdown field you want to add options for. The "value" represents the internal value that is sent when the user selects the option. The "label" is the option displayed to the user in the dropdown.</p>
<h2 id="handling-input-data-collected-from-the-dashboard-form" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#handling-input-data-collected-from-the-dashboard-form"></a> Handling Input Data Collected from the Dashboard Form</h2>
<p>When a user submits the dashboard form, the input data is sent to Node-RED, where it can be accessed and processed. This enables you to perform tasks such as validating the data, transforming it, or sending it to other systems like databases or APIs.</p>
<h3 id="retrieving-submitted-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#retrieving-submitted-data"></a> Retrieving Submitted Data</h3>
<p>The data submitted from the form is transmitted to any nodes connected to the output of the <code>ui-from</code> node, and is contained within <code>msg.payload</code>. Each field’s value can be accessed using the field’s key or name as the property within <code>msg.payload</code>.</p>
<p>For example, let’s say the form includes the following fields:</p>
<ul>
<li>Device Name (key: <code>device_name</code>)</li>
<li>Device ID (key: <code>device_id</code>)</li>
<li>Device Serial Number (key: <code>serial_number</code>)</li>
<li>Country (key: <code>country</code>)</li>
</ul>
<p>After submission, you can access these values like this in your flow:</p>
<ul>
<li>Device Name: <code>msg.payload.device_name</code></li>
<li>Device ID: <code>msg.payload.device_id</code></li>
<li>Device Serial Number: <code>msg.payload.serial_number</code></li>
<li>Country: <code>msg.payload.country</code></li>
</ul>
<p>You can use this data anywhere in your flow — for example, to save it in a database or store it in <a href="https://flowfuse.com/docs/user/persistent-context/#flowfuse-persistent-context">FlowFuse’s context storage</a>. Crucially, this collected data can also directly instruct machines on the shop floor, with FlowFuse Device Agent managing that precise control.</p>
<h2 id="building-your-dynamic-production-recipe-update-form" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#building-your-dynamic-production-recipe-update-form"></a> Building Your Dynamic Production Recipe Update Form</h2>
<p>In this section, you will build an advanced flow for dynamically updating production recipes using FlowFuse Forms.</p>
<p>A production recipe, often referred to as a manufacturing recipe or master batch record, is a critical set of instructions that defines the precise parameters, ingredients, and steps required to produce a specific product consistently. This includes details like material quantities, temperature, mixing speeds, pressures, and hold times.</p>
<p>This setup uses a <strong>ui-dropdown</strong> for selecting recipes and a <strong>ui-form</strong> that dynamically populates and allows updates to recipe parameters. Everything you have learned so far will come together here to create a practical and interactive solution.</p>
<h3 id="set-up-your-sqlite-database" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#set-up-your-sqlite-database"></a> Set Up Your SQLite Database</h3>
<p>Use the following flow to quickly set up your SQLite database. It creates a recipes table and populates it with demo data.</p>
<p><strong>Steps:</strong></p>
<ol>
<li>
<p>Import the flow into your Node-RED editor (from the example provided below).</p>
</li>
<li>
<p>Deploy the flow to activate it.</p>
</li>
<li>
<p>Click the Inject node labeled "Populate Demo Recipes" to insert the sample data.</p>
</li>
</ol>
<div id="nr-flow-186" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow186 = "\n[{\"id\":\"d248b8387940a5bc\",\"type\":\"group\",\"z\":\"295d40790bd21f48\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"b9693cc84311ae8e\",\"e7619cfe7c7fdaa9\",\"f9e35c9c3b213d47\",\"c719f2b43aef7d44\",\"69a914bb0479925c\",\"bb3da75dcf2b4cdd\"],\"x\":54,\"y\":79,\"w\":752,\"h\":162},{\"id\":\"b9693cc84311ae8e\",\"type\":\"sqlite\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"mydb\":\"5e345bf74f08f47c\",\"sqlquery\":\"fixed\",\"sql\":\"CREATE TABLE IF NOT EXISTS recipes (\\n recipe_id TEXT PRIMARY KEY NOT NULL, -- Unique internal identifier (e.g., 'PX-BLEND-V3')\\n product_name TEXT NOT NULL, -- Human-readable product name (e.g., 'Premium Polymer Blend')\\n version_no TEXT NOT NULL, -- Recipe version (e.g., '3.1', 'A-Rev')\\n target_temperature_c REAL NOT NULL, -- Target temperature in Celsius\\n mixing_speed_rpm INTEGER NOT NULL, -- Mixing speed in Revolutions Per Minute\\n pressure_bar REAL, -- Pressure in Bar (more common than PSI in many regions)\\n material_a_kg REAL NOT NULL, -- Quantity of main material A in kilograms\\n material_b_kg REAL, -- Quantity of secondary material B in kilograms (optional for some recipes)\\n catalyst_ml REAL, -- Quantity of catalyst in milliliters (specific additive)\\n hold_time_min INTEGER NOT NULL, -- Hold time in minutes at target temperature\\n description TEXT, -- Optional notes about the recipe\\n created_date TEXT NOT NULL -- Date recipe was created/last updated (ISO format)\\n);\",\"name\":\"\",\"x\":470,\"y\":120,\"wires\":[[\"f9e35c9c3b213d47\"]]},{\"id\":\"e7619cfe7c7fdaa9\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"name\":\"Create Recipe table\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":200,\"y\":120,\"wires\":[[\"b9693cc84311ae8e\"]]},{\"id\":\"f9e35c9c3b213d47\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":700,\"y\":120,\"wires\":[]},{\"id\":\"c719f2b43aef7d44\",\"type\":\"sqlite\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"mydb\":\"5e345bf74f08f47c\",\"sqlquery\":\"fixed\",\"sql\":\"INSERT INTO recipes (recipe_id, product_name, version_no, target_temperature_c, mixing_speed_rpm, pressure_bar, material_a_kg, material_b_kg, catalyst_ml, hold_time_min, description, created_date)\\nVALUES\\n('POLY_BLEND_V3.1', 'Advanced Polymer Resin', '3.1', 195.0, 850, 1.5, 1250.0, 450.0, 15.0, 60, 'Improved tensile strength for injection molding. Requires high shear.', '2025-01-10'),\\n('COATING_ECO_V1.2', 'Eco-Shield Protective Coating', '1.2', 110.0, 320, 0.8, 800.0, 200.0, 5.0, 30, 'Low VOC formulation, quick dry time. Mix gently.', '2024-11-22'),\\n('ADHESIVE_FAST_CURE', 'Industrial Adhesive X-500', '1.0', 70.0, 550, 2.1, 300.0, 120.0, 10.0, 15, 'Fast-curing formulation for rapid assembly. Must maintain precise temperature.', '2025-03-01'),\\n('FOOD_LIQUID_PURE', 'PureBev Beverage Base', '2.0', 85.0, 180, 0.5, 2000.0, 500.0, NULL, 45, 'Food-grade liquid base. Ensure sterile conditions. No catalyst used.', '2025-02-15'),\\n('PHARMA_API_MIX', 'API Compound Blend Alpha', '1.0', 45.0, 400, 1.2, 50.0, 25.0, 2.0, 90, 'Active Pharmaceutical Ingredient blend. Temperature sensitive. Strict hold time.', '2025-04-05');\",\"name\":\"\",\"x\":470,\"y\":200,\"wires\":[[\"bb3da75dcf2b4cdd\"]]},{\"id\":\"69a914bb0479925c\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"name\":\"Populate Demo Recipes\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":210,\"y\":200,\"wires\":[[\"c719f2b43aef7d44\"]]},{\"id\":\"bb3da75dcf2b4cdd\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"g\":\"d248b8387940a5bc\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":700,\"y\":200,\"wires\":[]},{\"id\":\"5e345bf74f08f47c\",\"type\":\"sqlitedb\",\"db\":\"/tmp/sqlite\",\"mode\":\"RWC\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow186.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-186') })</script>
<h3 id="initial-setup%3A-populate-dropdown-and-define-form-structure" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#initial-setup%3A-populate-dropdown-and-define-form-structure"></a> Initial Setup: Populate Dropdown and Define Form Structure</h3>
<p>We want this flow to run when our dashboard page loads. It queries your exiting recipes and then dynamically defines both the <strong>ui-dropdown's</strong> options and the <strong>ui-form's</strong> structure.</p>
<ol>
<li>
<p>Drag an Event node onto the canvas. This node send a message when the dashboard page loads.</p>
</li>
<li>
<p>Connect the <strong>ui-event</strong> node to an <strong>sqlite</strong> node. Configure it to connect to your SQLite database, set SQL Query to fixed, and enter <code>SELECT recipe_id FROM recipes;</code> as the query. Ensure Return Output is set to a "Parsed JSON Object".</p>
</li>
<li>
<p>Connect the <strong>sqlite</strong> node's output to a new <strong>function</strong> node. Name it "Generate Form & Dropdown Definition". In this function, you will write JavaScript to dynamically create the form's elements and populate the dropdown options. Set the function to have 2 outputs.</p>
</li>
</ol>
<div style="position: relative" id="code-container-525">
<pre class="language-javascript"><code id="code-525" class="language-javascript"><span class="token comment">// msg.payload contains the recipe_id and recipe_name from SQLite query.</span><br /><span class="token keyword">let</span> dropdownOptions <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload <span class="token operator">&&</span> Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> dropdownOptions <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">recipe</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> recipe<span class="token punctuation">.</span>recipe_id<span class="token punctuation">,</span> <span class="token comment">// Internal value for dropdown</span><br /> <span class="token literal-property property">label</span><span class="token operator">:</span> recipe<span class="token punctuation">.</span>recipe_name <span class="token comment">// Display text for dropdown</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// --- Define the ui_form structure (all input elements) ---</span><br /><span class="token keyword">let</span> formElements <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Recipe ID"</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"recipe_id_display"</span><span class="token punctuation">,</span> <span class="token literal-property property">readOnly</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Product Name"</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"product_name"</span><span class="token punctuation">,</span> <span class="token literal-property property">readOnly</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Version No."</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"version_no"</span><span class="token punctuation">,</span> <span class="token literal-property property">readOnly</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Target Temp (°C)"</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"target_temperature_c"</span><span class="token punctuation">,</span> <span class="token literal-property property">required</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Mixing Speed (RPM)"</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"mixing_speed_rpm"</span><span class="token punctuation">,</span> <span class="token literal-property property">required</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token literal-property property">label</span><span class="token operator">:</span> <span class="token string">"Pressure (Bar)"</span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">"pressure_bar"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token string-property property">"label"</span><span class="token operator">:</span> <span class="token string">"Material A (kg)"</span><span class="token punctuation">,</span> <span class="token string-property property">"key"</span><span class="token operator">:</span> <span class="token string">"material_a_kg"</span><span class="token punctuation">,</span> <span class="token string-property property">"required"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token string-property property">"label"</span><span class="token operator">:</span> <span class="token string">"Material B (kg)"</span><span class="token punctuation">,</span> <span class="token string-property property">"key"</span><span class="token operator">:</span> <span class="token string">"material_b_kg"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token string-property property">"label"</span><span class="token operator">:</span> <span class="token string">"Catalyst (ml)"</span><span class="token punctuation">,</span> <span class="token string-property property">"key"</span><span class="token operator">:</span> <span class="token string">"catalyst_ml"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"number"</span><span class="token punctuation">,</span> <span class="token string-property property">"label"</span><span class="token operator">:</span> <span class="token string">"Hold Time (min)"</span><span class="token punctuation">,</span> <span class="token string-property property">"key"</span><span class="token operator">:</span> <span class="token string">"hold_time_min"</span><span class="token punctuation">,</span> <span class="token string-property property">"required"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"multiline"</span><span class="token punctuation">,</span> <span class="token string-property property">"label"</span><span class="token operator">:</span> <span class="token string">"Description"</span><span class="token punctuation">,</span> <span class="token string-property property">"key"</span><span class="token operator">:</span> <span class="token string">"description"</span><span class="token punctuation">,</span> <span class="token string-property property">"readOnly"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token string-property property">"rows"</span><span class="token operator">:</span> <span class="token number">3</span> <span class="token punctuation">}</span><br /><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Output 1: For the ui_dropdown node (msg.options)</span><br /><span class="token keyword">let</span> msg1 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">options</span><span class="token operator">:</span> dropdownOptions <span class="token punctuation">}</span><span class="token punctuation">;</span> <br /><br /><span class="token comment">// Output 2: For the ui_form node (msg.ui_update.options)</span><br /><span class="token keyword">let</span> msg2 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">ui_update</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">options</span><span class="token operator">:</span> formElements <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">return</span> <span class="token punctuation">[</span>msg1<span class="token punctuation">,</span> msg2<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Send two separate messages</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-525" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>
<p>Drag a <strong>ui-dropdown</strong> node onto the canvas. Configure its dashboard group and label (Select Recipe ID:). Ensure its "Options" list is empty, as it will be populated dynamically. Connect the first output of the "Generate Form & Dropdown Definition" <strong>function</strong> to the input of this <strong>ui_dropdown</strong> node.</p>
</li>
<li>
<p>Drag a <strong>ui-form</strong> widget onto the canvas. Configure its dashboard group and label (Recipe Parameters). Crucially, leave its "Options" list completely empty in its properties. Set the "Submit" button text to Apply and "Cancel" to Clear. Connect the second output of the "Generate Form & Dropdown Definition" <strong>function</strong> to the input of this <strong>ui-form</strong> node.</p>
</li>
<li>
<p>Deploy your flow and open the dashboard. You should now see your form with the "Select Recipe" dropdown populated.</p>
</li>
</ol>
<h3 id="populate-form-on-recipe-selection" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#populate-form-on-recipe-selection"></a> Populate Form on Recipe Selection</h3>
<p>This flow segment pre-fills the form with recipe details when an operator selects a recipe from the dropdown.</p>
<ol>
<li>
<p>Connect the output of your dropdown node (from Step 2). This output will carry the selected <code>recipe_id</code> in <code>msg.payload</code>.</p>
</li>
<li>
<p>Connect the <strong>ui-dropdown</strong> output to a <strong>change</strong> node. Name it Set Params & Flow Context. Set <code>msg.params.$recipe_id</code> to <code>msg.payload</code> and <code>flow.selected_recipe_id</code> to <code>msg.payload</code>.</p>
</li>
<li>
<p>Connect the <strong>change</strong> node to an <strong>sqlite</strong> node. Configure it for your database, set SQL Query to prepared statement, and enter <code>SELECT * FROM recipes WHERE recipe_id = $recipe_id;</code> as the prepared statement. Ensure you add a rule in change node:</p>
<ul>
<li><code>msg.params.$recipe_id</code> to <code>msg.payload.recipe_id</code></li>
</ul>
</li>
<li>
<p>Connect the <strong>sqlite</strong> node's output to a <strong>function</strong> node. Name it Show values to form fields. This function will format the retrieved recipe details to pre-fill the form.</p>
</li>
</ol>
<div style="position: relative" id="code-container-578">
<pre class="language-javascript"><code id="code-578" class="language-javascript"><span class="token keyword">let</span> recipeDetails <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Get the first (and only) result</span><br /><br /><span class="token keyword">if</span> <span class="token punctuation">(</span>recipeDetails<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Map recipe details to the keys of your form elements for pre-filling.</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">recipe_id_display</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>recipe_id<span class="token punctuation">,</span> <span class="token comment">// For the display field in ui_form</span><br /> <span class="token literal-property property">product_name</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>product_name<span class="token punctuation">,</span><br /> <span class="token literal-property property">version_no</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>version_no<span class="token punctuation">,</span><br /> <span class="token literal-property property">target_temperature_c</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>target_temperature_c<span class="token punctuation">,</span><br /> <span class="token literal-property property">mixing_speed_rpm</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>mixing_speed_rpm<span class="token punctuation">,</span><br /> <span class="token literal-property property">pressure_bar</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>pressure_bar<span class="token punctuation">,</span><br /> <span class="token literal-property property">material_a_kg</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>material_a_kg<span class="token punctuation">,</span><br /> <span class="token literal-property property">material_b_kg</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>material_b_kg<span class="token punctuation">,</span><br /> <span class="token literal-property property">catalyst_ml</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>catalyst_ml<span class="token punctuation">,</span><br /> <span class="token literal-property property">hold_time_min</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>hold_time_min<span class="token punctuation">,</span><br /> <span class="token literal-property property">description</span><span class="token operator">:</span> recipeDetails<span class="token punctuation">.</span>description<br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> <span class="token comment">// If selection is cleared, prepare an empty payload (except for selected_recipe_id)</span><br /> msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-578" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Connect the output of the Show values to form fields <strong>function</strong> node back to the input of your form widget (from Step 2).</li>
</ol>
<h3 id="handle-form-submission-%26-update-database" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#handle-form-submission-%26-update-database"></a> Handle Form Submission & Update Database</h3>
<p>This flow segment processes the data when the operator clicks the "Apply" button, updating the recipe in your database and providing feedback.</p>
<ol>
<li>
<p>Connect a new wire from the main output of your <strong>ui-form</strong> widget. This output fires when the form is submitted.</p>
</li>
<li>
<p>Connect the form's output to a <strong>change</strong> node. Name it Prepare Update Params. This node will prepare the <code>msg.params</code> object for the SQLite update.</p>
</li>
</ol>
<ul>
<li>Rules:
<ul>
<li>set <code>msg.params</code> to JSON <code>{}</code>.</li>
<li>set <code>msg.params.$recipe_id</code> to <code>flow.selected_recipe_id</code>.</li>
</ul>
</li>
</ul>
<p>For each editable field in your form (e.g., target_temperature_c, mixing_speed_rpm), add a rule: set <code>msg.params.$[FIELD_NAME] to msg.payload.[FIELD_NAME]</code>.</p>
<ol start="3">
<li>
<p>Connect the <strong>change</strong> node to an <strong>sqlite</strong> node.</p>
<ul>
<li>Configure it for your database, set SQL Query to prepared statement.</li>
<li>Paste your UPDATE SQL query into the "Prepared Statement" field, using the $parameters that match your msg.params.</li>
</ul>
</li>
<li>
<p>Connect the <strong>sqlite</strong> node's output to a <strong>switch</strong> node. Name it Check for Update Success.</p>
</li>
</ol>
<ul>
<li>Set Property to payload and Rules to is empty.</li>
<li>Add 1 output.</li>
</ul>
<ol start="5">
<li>Connect the <strong>switch</strong> node's output to a <strong>change</strong> node. Name it Success Message.</li>
</ol>
<ul>
<li>Set <code>msg.payload</code> to str "Recipe updated successfully".</li>
</ul>
<ol start="6">
<li>Connect the <strong>change</strong> node to a <strong>ui-notification</strong> node to display the success message on the dashboard.</li>
<li>Deploy the flow, open the dashboard, and try selecting different recipes and updating them.</li>
</ol>
<p>For practice, we use an SQLite database. However, since your recipe is often used across an entire production line, it is recommended to store it in a dedicated database instead of locally in SQLite. This ensures it is accessible to all systems and can be utilized by other components in the workflow.</p>
<p><em>Note: This is just a simple demo we built. When using it in a production environment, you might need to make additional considerations based on your specific requirements."</em></p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/recipe-update-form-lzLgWvapa7-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse form designed for updating production recipes. The form shows a dropdown for recipe selection, dynamically populates fields with recipe parameters, and allows the user to modify and submit updates." loading="lazy" decoding="async" src="https://flowfuse.com/img/recipe-update-form-lzLgWvapa7-800.webp" width="800" height="415" /></picture>
<em>A demonstration of the dynamic <strong>form for recipe updates</strong> in action, showing how it streamlines data entry and submission.</em></p>
<p>Below is the complete flow of the system we built.</p>
<div id="nr-flow-187" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow187 = "\n[{\"id\":\"a6eb2848159c68f4\",\"type\":\"ui-form\",\"z\":\"295d40790bd21f48\",\"name\":\"Form\",\"group\":\"83b5766434e42005\",\"label\":\"\",\"order\":2,\"width\":0,\"height\":0,\"options\":[{\"label\":\"demo\",\"key\":\"demo\",\"type\":\"text\",\"required\":false,\"rows\":null}],\"formValue\":{\"demo\":\"\"},\"payload\":\"\",\"submit\":\"Apply\",\"cancel\":\"Clear\",\"resetOnSubmit\":true,\"topic\":\"topic\",\"topicType\":\"msg\",\"splitLayout\":\"\",\"className\":\"\",\"passthru\":false,\"dropdownOptions\":[],\"x\":1170,\"y\":800,\"wires\":[[\"26303a8d6785dc68\"]]},{\"id\":\"5b2f1f1e42391625\",\"type\":\"function\",\"z\":\"295d40790bd21f48\",\"name\":\"Generate Form & Dropdown Definition\",\"func\":\"// msg.payload contains the recipe_id and recipe_name from SQLite query.\\nlet dropdownOptions = [];\\nif (msg.payload && Array.isArray(msg.payload)) {\\n dropdownOptions = msg.payload.map(recipe => {\\n return {\\n value: recipe.recipe_id, // Internal value for dropdown\\n label: recipe.recipe_name // Display text for dropdown\\n };\\n });\\n}\\n\\n// --- Define the ui_form structure (all input elements) ---\\nlet formElements = [\\n { type: \\\"text\\\", label: \\\"Product Name\\\", key: \\\"product_name\\\", readOnly: true },\\n { type: \\\"text\\\", label: \\\"Version No.\\\", key: \\\"version_no\\\", readOnly: true },\\n { type: \\\"number\\\", label: \\\"Target Temp (°C)\\\", key: \\\"target_temperature_c\\\", required: true },\\n { type: \\\"number\\\", label: \\\"Mixing Speed (RPM)\\\", key: \\\"mixing_speed_rpm\\\", required: true },\\n { type: \\\"number\\\", label: \\\"Pressure (Bar)\\\", key: \\\"pressure_bar\\\" },\\n { type: \\\"number\\\", \\\"label\\\": \\\"Material A (kg)\\\", \\\"key\\\": \\\"material_a_kg\\\", \\\"required\\\": true },\\n { type: \\\"number\\\", \\\"label\\\": \\\"Material B (kg)\\\", \\\"key\\\": \\\"material_b_kg\\\" },\\n { type: \\\"number\\\", \\\"label\\\": \\\"Catalyst (ml)\\\", \\\"key\\\": \\\"catalyst_ml\\\" },\\n { type: \\\"number\\\", \\\"label\\\": \\\"Hold Time (min)\\\", \\\"key\\\": \\\"hold_time_min\\\", \\\"required\\\": true },\\n { type: \\\"multiline\\\", \\\"label\\\": \\\"Description\\\", \\\"key\\\": \\\"description\\\", \\\"readOnly\\\": true, \\\"rows\\\": 3 }\\n];\\n\\n// Output 1: For the ui_dropdown node (msg.options)\\nlet msg1 = { options: dropdownOptions };\\n\\n// Output 2: For the ui_form node (msg.ui_update.options)\\nlet msg2 = { ui_update: { options: formElements } };\\n\\nreturn [msg1, msg2]; // Send two separate messages\",\"outputs\":2,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":870,\"y\":780,\"wires\":[[\"80a5a9f40e19f5a7\"],[\"a6eb2848159c68f4\"]]},{\"id\":\"9038572da428216c\",\"type\":\"sqlite\",\"z\":\"295d40790bd21f48\",\"mydb\":\"5e345bf74f08f47c\",\"sqlquery\":\"prepared\",\"sql\":\"SELECT * FROM recipes WHERE recipe_id = $recipe_id;\",\"name\":\"\",\"x\":1610,\"y\":760,\"wires\":[[\"462db842ba535af6\"]]},{\"id\":\"c2536d7e7a260bc1\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"params\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"params.$recipe_id\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"selected_recipe_id\",\"pt\":\"flow\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1420,\"y\":760,\"wires\":[[\"9038572da428216c\"]]},{\"id\":\"462db842ba535af6\",\"type\":\"function\",\"z\":\"295d40790bd21f48\",\"name\":\"Show values to form fields\",\"func\":\"// msg.payload will be an array with one object: [{ recipe_id: '...', recipe_name: '...', ... }]\\nlet recipeDetails = msg.payload[0]; // Get the first (and only) result\\n\\nif (recipeDetails) {\\n // Map recipe details directly to the keys of your form elements\\n // This msg.payload will be sent to the ui_form to pre-fill its fields\\n msg.payload = {\\n selected_recipe_id: recipeDetails.recipe_id, // Keep the dropdown selected\\n product_name: recipeDetails.product_name,\\n version_no: recipeDetails.version_no,\\n target_temperature_c: recipeDetails.target_temperature_c,\\n mixing_speed_rpm: recipeDetails.mixing_speed_rpm,\\n pressure_bar: recipeDetails.pressure_bar,\\n material_a_kg: recipeDetails.material_a_kg,\\n material_b_kg: recipeDetails.material_b_kg,\\n catalyst_ml: recipeDetails.catalyst_ml,\\n hold_time_min: recipeDetails.hold_time_min,\\n description: recipeDetails.description\\n };\\n} else {\\n // Clear non-dropdown fields if no recipe found (e.g., if dropdown cleared)\\n let currentSelection = msg.payload.selected_recipe_id;\\n msg.payload = { selected_recipe_id: currentSelection }; // Keep dropdown value but clear others\\n}\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1840,\"y\":760,\"wires\":[[\"9ff423aae018ab04\"]]},{\"id\":\"e495f9bc79062be3\",\"type\":\"sqlite\",\"z\":\"295d40790bd21f48\",\"mydb\":\"5e345bf74f08f47c\",\"sqlquery\":\"fixed\",\"sql\":\"SELECT recipe_id FROM recipes;\",\"name\":\"\",\"x\":570,\"y\":780,\"wires\":[[\"5b2f1f1e42391625\"]]},{\"id\":\"9d6013fa166356cf\",\"type\":\"ui-event\",\"z\":\"295d40790bd21f48\",\"ui\":\"ee052dbdb58cf632\",\"name\":\"\",\"x\":400,\"y\":780,\"wires\":[[\"e495f9bc79062be3\"]]},{\"id\":\"80a5a9f40e19f5a7\",\"type\":\"ui-dropdown\",\"z\":\"295d40790bd21f48\",\"group\":\"83b5766434e42005\",\"name\":\"Dropdown\",\"label\":\"Select Recipe ID:\",\"tooltip\":\"\",\"order\":1,\"width\":0,\"height\":0,\"passthru\":false,\"multiple\":false,\"chips\":false,\"clearable\":false,\"options\":[{\"label\":\"\",\"value\":\"\",\"type\":\"str\"}],\"payload\":\"\",\"topic\":\"topic\",\"topicType\":\"msg\",\"className\":\"\",\"typeIsComboBox\":true,\"msgTrigger\":\"onChange\",\"x\":1190,\"y\":760,\"wires\":[[\"c2536d7e7a260bc1\"]]},{\"id\":\"9ff423aae018ab04\",\"type\":\"link out\",\"z\":\"295d40790bd21f48\",\"name\":\"link out 1\",\"mode\":\"link\",\"links\":[\"a4c1676c7c656ff3\"],\"x\":2025,\"y\":760,\"wires\":[]},{\"id\":\"a4c1676c7c656ff3\",\"type\":\"link in\",\"z\":\"295d40790bd21f48\",\"name\":\"link in 1\",\"links\":[\"9ff423aae018ab04\"],\"x\":905,\"y\":820,\"wires\":[[\"a6eb2848159c68f4\"]]},{\"id\":\"26303a8d6785dc68\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"params\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"params.$recipe_id\",\"pt\":\"msg\",\"to\":\"selected_recipe_id\",\"tot\":\"flow\"},{\"t\":\"set\",\"p\":\"params.$product_name\",\"pt\":\"flow\",\"to\":\"payload.product_name\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$version_no\",\"pt\":\"msg\",\"to\":\"payload.version_no\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$target_temperature_c\",\"pt\":\"msg\",\"to\":\"payload.target_temperature_c\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$mixing_speed_rpm\",\"pt\":\"msg\",\"to\":\"payload.mixing_speed_rpm\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$pressure_bar\",\"pt\":\"msg\",\"to\":\"payload.pressure_bar\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$material_a_kg\",\"pt\":\"msg\",\"to\":\"payload.material_a_kg\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$material_b_kg\",\"pt\":\"msg\",\"to\":\"payload.material_b_kg\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$catalyst_ml\",\"pt\":\"msg\",\"to\":\"payload.catalyst_ml\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$hold_time_min\",\"pt\":\"msg\",\"to\":\"payload.hold_time_min\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$description\",\"pt\":\"msg\",\"to\":\"payload.description\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1420,\"y\":800,\"wires\":[[\"656fb4832e9224b7\"]]},{\"id\":\"656fb4832e9224b7\",\"type\":\"sqlite\",\"z\":\"295d40790bd21f48\",\"mydb\":\"5e345bf74f08f47c\",\"sqlquery\":\"prepared\",\"sql\":\"-- Update key parameters for a specific recipe\\nUPDATE recipes\\nSET \\n target_temperature_c = $target_temperature_c,\\n mixing_speed_rpm = $mixing_speed_rpm,\\n pressure_bar = $pressure_bar,\\n material_a_kg = $material_a_kg,\\n material_b_kg = $material_b_kg,\\n catalyst_ml = $catalyst_ml,\\n hold_time_min = $hold_time_min,\\n description = $description,\\n version_no = $version_no\\nWHERE recipe_id = $recipe_id\",\"name\":\"\",\"x\":1610,\"y\":800,\"wires\":[[\"e82526d367b028b3\"]]},{\"id\":\"81a84dcab301a18f\",\"type\":\"ui-notification\",\"z\":\"295d40790bd21f48\",\"ui\":\"ee052dbdb58cf632\",\"position\":\"center center\",\"colorDefault\":true,\"color\":\"#000000\",\"displayTime\":\"3\",\"showCountdown\":true,\"outputs\":1,\"allowDismiss\":true,\"dismissText\":\"Close\",\"allowConfirm\":false,\"confirmText\":\"Confirm\",\"raw\":false,\"className\":\"\",\"name\":\"\",\"x\":2110,\"y\":800,\"wires\":[[]]},{\"id\":\"e82526d367b028b3\",\"type\":\"switch\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"empty\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":1770,\"y\":800,\"wires\":[[\"20e94224438447c1\"]]},{\"id\":\"20e94224438447c1\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"Recipe updated successfully.\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1920,\"y\":800,\"wires\":[[\"81a84dcab301a18f\"]]},{\"id\":\"83b5766434e42005\",\"type\":\"ui-group\",\"name\":\"Recipe Form\",\"page\":\"a9aa17e3cfcd76d0\",\"width\":6,\"height\":1,\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\",\"groupType\":\"default\"},{\"id\":\"5e345bf74f08f47c\",\"type\":\"sqlitedb\",\"db\":\"/tmp/sqlite\",\"mode\":\"RWC\"},{\"id\":\"ee052dbdb58cf632\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"showPathInSidebar\":false,\"headerContent\":\"page\",\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\",\"showReconnectNotification\":true,\"notificationDisplayTime\":1,\"showDisconnectNotification\":true,\"allowInstall\":true},{\"id\":\"a9aa17e3cfcd76d0\",\"type\":\"ui-page\",\"name\":\"Recipe\",\"ui\":\"ee052dbdb58cf632\",\"path\":\"/recipe\",\"icon\":\"home\",\"layout\":\"notebook\",\"theme\":\"326855cf654199bc\",\"breakpoints\":[{\"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\":\"326855cf654199bc\",\"type\":\"ui-theme\",\"name\":\"Default Theme\",\"colors\":{\"surface\":\"#222322\",\"primary\":\"#222322\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"density\":\"default\",\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow187.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-187') })</script>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-forms-easy-data-collection-factory-floor/#conclusion"></a> Conclusion</h2>
<p>So, getting your factory data digital doesn't have to be a headache. Relying on paper or tricky old systems just causes slowdowns and mistakes. Plus, many digital form tools are too complicated or don't play nice with your current setup.</p>
<p>That's where FlowFuse comes in. It lets your engineers build exactly what they need for the factory, using simple drag-and-drop tools – no coding required. This means you can ditch the manual steps, cut down on errors, save time, and even lower your IT costs.</p>
<p>Also wiith FlowFuse, you get accurate, real-time data and better control, helping your factory run smarter and much more efficiently.</p>
<p><em>Want to see how FlowFuse can reduce costs, boost profits, and increase production? <a href="https://flowfuse.com/contact-us/">Get in touch with us.</a></em></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/MES: What It Is and Why You Need It for Your FactoryMES Explained: Essential Insights for Factory Operations2025-06-05T00:00:00ZSumit Shinde<p>Running a factory means constantly pushing for better production. You want to make more products, make them faster, and ensure they're all high quality. But often, the challenge is simply knowing what's actually happening on the factory floor, right now. Is a machine broken? Are we on track with our orders? Is the quality holding up? Getting these answers often involves manual checks, waiting for reports, or just guessing.</p>
<!--more-->
<p>A Manufacturing Execution System (MES) solves this. It's the central system that connects your factory's production to its plans. It gives you clear, instant visibility and control over every step on your shop floor.</p>
<p>Whether you've managed factory operations for years or are just starting to understand how they work, the need for this kind of clarity is universal.</p>
<p>In this article, our goal is to make sure you clearly understand exactly what an MES is and, just as importantly, what it isn't. We'll explore why it's a critical tool for any modern factory that wants to produce better, faster, and with fewer problems, and we'll look at its major components.</p>
<h2 id="what-is-mes%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#what-is-mes%3F"></a> What is MES?</h2>
<p>A Manufacturing Execution System (MES) is the operational backbone of a modern factory. It's not merely a software program, but a dynamic, integrated system designed to bridge the crucial gap between your enterprise-level business planning and the real-time execution of production on the shop floor.</p>
<p>Think of MES as the intelligent conductor of your manufacturing orchestra. It orchestrates all the elements of production – machines, materials, people, and processes – in real-time, ensuring that your strategic plans translate into tangible products efficiently and effectively.</p>
<p>The MES collects real-time information to tell you:</p>
<ul>
<li>What products are currently being built.</li>
<li>How many of them are finished.</li>
<li>Which machines are working, and if any are having issues.</li>
<li>What materials are being used up.</li>
<li>If the quality of the products is good, right as they're being made.</li>
</ul>
<p>This means you always know the exact status of your production, live. Such real-time insights and operational control are at the core of what a modern MES delivers, often leveraging flexible platforms like FlowFuse to connect diverse factory data and orchestrate workflows.</p>
<h2 id="what-mes-is-not" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#what-mes-is-not"></a> What MES is NOT</h2>
<p>It's easy to get confused about all the different computer systems in a factory. To really get what a MES does, it helps to know what it isn't.</p>
<p>Many people think an MES is the same as their ERP system. But that's not quite right. Your ERP (Enterprise Resource Planning) handles the big company plans like money, sales orders, and buying materials for the long run. An MES doesn't do this planning. Its job is focused purely on the factory floor, managing what's happening right now to build products. It makes sure the production plans actually get done.</p>
<p>Another common confusion is that an MES is like a SCADA or PLC system. Again, this isn't accurate. SCADA (Supervisory Control and Data Acquisition) and PLC (Programmable Logic Controller) systems are connected directly to individual machines. They tell machines what to do—like turning them on or off—and collect basic data from them. An MES doesn't control machines directly. Instead, the MES acts like the main manager for the whole factory's production. It gathers information from those machine controllers (SCADA/PLCs) and uses it to oversee the entire process, including guiding workers, checking quality, and making sure all the machines work together to finish an order.</p>
<p>So, while ERP plans the business, and SCADA/PLC run the machines, the MES is the crucial system that manages the actual production process that happens in between.</p>
<h2 id="where-does-mes-sit-in-your-factory%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#where-does-mes-sit-in-your-factory%3F"></a> Where Does MES Sit in Your Factory?</h2>
<p>To truly get the clear view and control you need over your factory's operations, it helps to understand exactly where a MES fits. Your factory's computer systems are often set up in different layers, a common way to organize them is by following the ISA-95 standard.</p>
<p>Your MES sits right in the crucial middle layer. It acts as the key link between the big plans made higher up and the actual machines doing the work.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/SA-95-hierarchical-view-of-automation-infrastructures-5XTPQ3BB52-850.avif 850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/SA-95-hierarchical-view-of-automation-infrastructures-5XTPQ3BB52-850.webp 850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="MES System in the isa-95 layers" loading="lazy" decoding="async" src="https://flowfuse.com/img/SA-95-hierarchical-view-of-automation-infrastructures-5XTPQ3BB52-850.jpeg" width="850" height="373" /></picture>
<em>[MES System in the isa-95 layers]</em></p>
<p>At the very top, you have your main planning system, usually an ERP. The MES takes the plans from this ERP. Its job is to ensure they are carried out perfectly on the factory floor, moment by moment, guiding and watching every single step of making your products. Below the MES are systems like SCADA. The MES tells these systems what to do with the machines, and they send live information back, giving you an instant picture of production.</p>
<p>So, the MES is the central connection. It joins your company's big plans with the exact, real-time work on the factory floor</p>
<h2 id="why-your-factory-needs-mes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#why-your-factory-needs-mes"></a> Why Your Factory Needs MES</h2>
<p>Your factory needs to do more than just make products. It needs to stay ahead of rivals, innovate, and boost profits. An MES helps your factory achieve these important goals.</p>
<p>Here's why an MES is vital for your factory:</p>
<ul>
<li><strong>You'll always be ready:</strong> Instead of just fixing problems, you'll see them coming. This helps you make things better and react quickly to changes.</li>
<li><strong>Top quality, every time:</strong> An MES helps you check and control quality at each step. This means fewer mistakes, and customers will trust your brand more.</li>
<li><strong>Make the most of everything:</strong> You'll use your machines, materials, and people better. This cuts down on waste and makes sure every part of your factory works at its best.</li>
<li><strong>Easier to innovate:</strong> With good data and clear processes, you can try new ideas and bring better products to market faster.</li>
<li><strong>Increased profits:</strong> By making things more efficiently, cutting errors, and delivering on time, an MES directly helps your factory earn more money and grow.</li>
</ul>
<p>An MES isn't just another system. It's a key tool that helps your factory become smarter, more responsive, and more profitable.</p>
<h2 id="essential-modules-of-mes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#essential-modules-of-mes"></a> Essential Modules of MES</h2>
<p>As mentioned earlier, MES isn't just one big piece of software; it's made up of several important parts that work together to manage your factory floor.</p>
<p>One key part helps with <strong>scheduling and dispatching production</strong> – it decides what specific jobs need to be done, when, and on which machines. Another important piece is for <strong>managing all your resources</strong>, meaning it keeps track of your equipment, tools, and even your people, making sure everything is available when needed.</p>
<p>Then there's the part that handles <strong>data collection</strong>, gathering all the live information directly from machines and sensors on the floor. This ties into <strong>quality management</strong>, which makes sure products meet standards at every step by guiding checks and recording results. An MES also includes <strong>product traceability</strong>, building a complete history for every item, so you always know what went into it and how it was made. Finally, there are components for <strong>performance analysis</strong>, showing you how well your production is running, and often for <strong>maintenance management</strong> to help keep machines in good working order.</p>
<p>These parts all connect to give you full control and insight into your manufacturing process.</p>
<h2 id="major-challenges-with-mes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#major-challenges-with-mes"></a> Major Challenges with MES</h2>
<p>While an MES offers huge benefits, putting one in place and getting the most out of it can have its challenges. It's important to know what these might be so you can plan for them.</p>
<p>First and foremost challenge is cost and time to implement MES. Investing in an MES can be expensive upfront, including the software, hardware, and the considerable time it takes to set it up correctly across your factory. Beyond that, getting all your machines to talk to the MES can be complex, especially with older equipment from different manufacturers – it's like getting everyone to speak a single language. Another hurdle is changing how people work; employees may need significant training and can resist new ways of doing things. Also, choosing the right MES system from many options to perfectly fit your factory's unique needs can be tough. Finally, an MES needs ongoing care, updates, and adjustments as your factory changes. Without this, it might become outdated. Plus, the system is only as good as the data it gets; if the information isn't accurate, the MES won't provide reliable insights, making data quality a continuous effort.</p>
<h2 id="how-flowfuse-solves-these-challenges" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/what-is-mes/#how-flowfuse-solves-these-challenges"></a> How FlowFuse Solves These Challenges</h2>
<p>So, how can we solve these problems, especially the ones related to getting started, integrating machines, and training people? This is exactly where FlowFuse steps in as a powerful solution. FlowFuse is a platform which directly addresses several of the major challenges we just discussed. Its visual, "drag and drop" interface means that engineers and factory personnel can build and deploy solutions much faster, significantly cutting down on the time and specialized talent usually needed for MES implementation and customization. This reduces the need for complex coding or developing deep programming skills.</p>
<p>FlowFuse also excels at connecting diverse systems. Its vast library of nodes allows for easy integration with a wide range of industrial equipment, sensors, and existing IT systems, solving the complex problem of getting older machines or systems from different vendors to "talk" to each other without extensive custom development. This also enhances flexibility, as you can easily adapt and extend your MES functionality as your factory needs evolve. Furthermore, ensuring good data quality is made easier with the platform's powerful data transformation capabilities, allowing you to clean, filter, and structure raw data effortlessly, ensuring your MES always operates with accurate insights. By simplifying integration and development, and reducing the reliance on highly specialized coders, FlowFuse can dramatically lower the overall cost of implementing and maintaining an MES.</p>
<p>In our upcoming articles, we will go deeper. We plan to explain and practically demonstrate how FlowFuse can be used to build your own MES, giving you the control and flexibility you need without the typical high investment or the need to buy a pre-packaged system. Stay tuned to see how you can create a tailored MES solution for your factory with FlowFuse.</p>
<p>If you're ready to explore how FlowFuse can help you build a modern MES tailored to your factory, <a href="https://flowfuse.com/contact-us/">get in touch</a> with our team today. We'd love to learn about your needs and help you take the next step toward a more efficient and profitable factory.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/FlowFuse 2.18: Smarter Monitoring, AI Integration, Improved DevOps, and a preview of exciting things to comeMonitor and improve instance performance, run AI chat in your Dashboard, Git pull, and more.2025-06-05T00:00:00ZGreg Stoutenburg<p>This release is focused on improvements that help you manage and optimize the performance of your Node-RED instances and takes an important step in integrating AI with FlowFuse so that you can build applications even more quickly.</p>
<!--more-->
<h2 id="enhanced-observability-for-better-performance-management" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#enhanced-observability-for-better-performance-management"></a> Enhanced Observability for Better Performance Management</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/observability1-i4_K-xTSTC-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/observability1-i4_K-xTSTC-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Performance feature" loading="lazy" decoding="async" src="https://flowfuse.com/img/observability1-i4_K-xTSTC-650.jpeg" width="650" height="311" /></picture>
<em>Screenshot of Performance Feature</em></p>
<p>Understanding how your Node-RED instances perform is crucial for maintaining reliable applications. Our new observability feature provides detailed CPU usage metrics at both the instance and team levels, giving you the visibility needed to optimize performance and troubleshoot issues before they impact your operations.</p>
<p>With these insights, you can make informed decisions about scaling your instances, identify performance bottlenecks, and ensure your Node-RED instances run smoothly in production environments. This feature is available exclusively for Enterprise customers, providing the enterprise-grade monitoring capabilities your organization needs.</p>
<h2 id="blueprint%3A-openai-llm-with-chat-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#blueprint%3A-openai-llm-with-chat-agent"></a> Blueprint: OpenAI LLM with Chat Agent</h2>
<p><video src="https://website-data.s3.eu-west-1.amazonaws.com/Blueprint+-+Open+AI+Chat.mp4" controls=""></video>
<em>Video of OpenAI LLM Blueprint demo</em></p>
<p>We're bringing AI speed and power directly to your FlowFuse Dashboard. The new LLM Blueprint enables you to deploy an AI chat agent that can query and analyze data connected to your FlowFuse environment.</p>
<p>This Blueprint makes it simple to surface insights relevant to your Node-RED flows, allowing team members to ask natural language questions and get immediate answers about their connected systems and devices. Whether you're monitoring sensor data, analyzing trends, or troubleshooting issues, the AI chat agent will speed up your workflow.</p>
<p>Check out the video demo to see it in action, featuring the agent connected to a worldmap node!</p>
<p>To put this Blueprint to use, check out the Blueprint page for <a href="https://flowfuse.com/blueprints/ai/llm-chat-agent/">OpenAI LLM Chat Agent.</a></p>
<h2 id="complete-git-integration-with-pull-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#complete-git-integration-with-pull-support"></a> Complete Git Integration with Pull Support</h2>
<p>Building on our previous Git push functionality, we've now added Git pull support, completing the core Git integration experience within FlowFuse.</p>
<p>You can now seamlessly synchronize changes from your remote repositories, collaborate more effectively with team members, and maintain consistent version history across your Node-RED projects.</p>
<p>More details are available in the <a href="https://flowfuse.com/changelog/2025/06/git-integration/">Git Integration changelog</a>.</p>
<h2 id="self-hosted-blueprint-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#self-hosted-blueprint-support"></a> Self-Hosted Blueprint Support</h2>
<p>Organizations running self-hosted FlowFuse installations can now take advantage of the Blueprints we publish, bringing the same rapid development capabilities to on-premises and private cloud deployments.</p>
<p>Self-hosted installations will automatically pull down the <a href="https://flowfuse.com/blueprints/">blueprint library</a>, and will stay up to date when we publish new blueprints.</p>
<p>This update ensures that all FlowFuse users, regardless of their deployment model, can benefit from our growing library of pre-built solutions.</p>
<h2 id="where-are-we-headed%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#where-are-we-headed%3F"></a> Where are we headed?</h2>
<p>Our Engineering team is hard at work on the next major developments in bringing the speed of AI to FlowFuse, developing AI functionality that will be integrated directly into the Node-RED editor. This upcoming feature set will dramatically accelerate your development process, helping you build and deploy applications faster than ever before.</p>
<p>By combining AI assistance with Node-RED's visual programming approach, we're creating a development experience that's both more intuitive for newcomers and more powerful for experienced developers.</p>
<p>Here is a sneak peek of something we're working on: an AI chat in the Node-RED editor that allows you to ask questions about the instance you are working in.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/AI_preview-Z_V0qTceaS-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Preview of AI in Node-RED Editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/AI_preview-Z_V0qTceaS-650.webp" width="650" height="454" /></picture></p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#what-else-is-new%3F"></a> What else is new?</h2>
<p>For a full list of everything that went into our 2.18 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Which feature do you think you're most likely to use? Email me directly and let me know! You can reach me at greg@flowfuse.com.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/flowfuse-release-2-18/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/Connect Your Shop Floor to Your ERP – Odoo EditionTransform your operations by bringing real-time shop floor data directly into your ERP2025-06-02T00:00:00ZSumit Shinde<p>A major problem in manufacturing today is when your ERP system isn't getting real-time information from the factory floor. This gap causes big issues, like ordering too much material because inventory numbers are old, or missing important production deadlines. This lack of instant, correct information directly leads to higher costs and lost opportunities for your business.</p>
<!--more-->
<p>This problem often comes from old ways of recording data. Many factories still rely on paperwork, which takes up valuable space, and manual entries. These methods require extra human effort, causing big delays and added costs. These old, error-prone ways are simply not sustainable for modern manufacturing.</p>
<p>This post gives you a vital solution. We'll show you how to connect your factory floor directly to your ERP system using FlowFuse. This way, you'll see near real-time data in your ERP, helping you avoid issues such as costly over-orders, missed deadlines, and the burdens of excessive paperwork. For this article, we'll focus on integrating with the popular ERP system, Odoo.</p>
<h2 id="basic-demo%3A-automating-production-data-from-the-shop-floor-to-odoo-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#basic-demo%3A-automating-production-data-from-the-shop-floor-to-odoo-with-flowfuse"></a> Basic Demo: Automating Production Data from the Shop Floor to Odoo with FlowFuse</h2>
<p>Before we start diving into how you can connect your shop floor to ERP, let's first see a simple demo. This basic demo shows how FlowFuse acts as a smart link, making sure your production line data is always accurate in your ERP. We're doing this with a Raspberry Pi running the <a href="https://flowfuse.com/product/device-agent/">FlowFuse Agent</a>, which talks to a Siemens S7 PLC. A counter in the PLC, which ticks up every second to act like products being made, is precisely read using the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/">S7 protocol</a> through FlowFuse. This accurate count is then automatically sent to Odoo as the quantity for a "table leg product," keeping your inventory data always up-to-date.</p>
<p>This is just a simple example of what FlowFuse can do. But it has much more power! Imagine FlowFuse also checking your production orders (MOs) in your ERP to see what you need to make. It can look at your Bills of Material (BOMs) in your ERP to figure out all the parts required. If it sees you're short on something, it can automatically create purchase orders in your ERP to buy the missing parts. It can even make new manufacturing orders for components you need to build.</p>
<p><lite-youtube videoid="bxVq_8m-GOk" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<p>Below is the complete flow for this demo, in case you would like to explore it further or try it out yourself after reading the article</p>
<div id="nr-flow-179" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow179 = "\n[{\"id\":\"cab977c8e0d1c054\",\"type\":\"group\",\"z\":\"FFF0000000000001\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"6ba9343481e3b041\",\"8a3adb31adbb7475\",\"bfe37a946a6ed20a\",\"304719d8be3f5694\"],\"x\":664,\"y\":399,\"w\":732,\"h\":122},{\"id\":\"6ba9343481e3b041\",\"type\":\"odoo-xmlrpc-update\",\"z\":\"FFF0000000000001\",\"g\":\"cab977c8e0d1c054\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"product.template\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":1270,\"y\":480,\"wires\":[[]]},{\"id\":\"8a3adb31adbb7475\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"g\":\"cab977c8e0d1c054\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[[38],{\\\"qty_available\\\":payload}]\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1020,\"y\":480,\"wires\":[[\"6ba9343481e3b041\"]]},{\"id\":\"bfe37a946a6ed20a\",\"type\":\"debug\",\"z\":\"FFF0000000000001\",\"g\":\"cab977c8e0d1c054\",\"name\":\"Good Product Produced\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1050,\"y\":440,\"wires\":[]},{\"id\":\"304719d8be3f5694\",\"type\":\"s7 in\",\"z\":\"FFF0000000000001\",\"g\":\"cab977c8e0d1c054\",\"endpoint\":\"142c103a7735ea99\",\"mode\":\"single\",\"variable\":\"Counter\",\"diff\":true,\"name\":\"S7 \",\"x\":740,\"y\":480,\"wires\":[[\"8a3adb31adbb7475\",\"bfe37a946a6ed20a\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"\",\"db\":\"\",\"username\":\"\",\"password\":\"\"},{\"id\":\"142c103a7735ea99\",\"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\":\"\",\"vartable\":[{\"addr\":\"DB1,X4.0\",\"name\":\"Trigger\"},{\"addr\":\"DB1,DW0\",\"name\":\"Counter\"}]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow179.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-179') })</script>
<h2 id="getting-data-into-and-out-of-odoo-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#getting-data-into-and-out-of-odoo-with-flowfuse"></a> Getting Data Into and Out of Odoo with FlowFuse</h2>
<p>In this section, we will show you how you can connect your ERP (Odoo) with your shop floor using FlowFuse. This connection lets you read information, create new records, update existing ones, and search for specific data, bringing real-time factory insights right into your business system.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#prerequisites"></a> Prerequisites</h3>
<p>Before you begin, make sure you have the following:</p>
<ul>
<li><strong>Running FlowFuse Instance:</strong> Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out our <a href="https://app.flowfuse.com/account/create">free trial</a>.</li>
<li><strong>node-red-contrib-odoo-xmlrpc-filters-fields:</strong> Ensure you have <a href="https://flows.nodered.org/node/node-red-contrib-odoo-xmlrpc-filters-fields">node-red-contrib-odoo-xmlrpc-filters-fields</a> installed. This package will enable operations like reading, creating, updating, and searching data, with specific capabilities for filtering records and selecting precise fields.</li>
</ul>
<h3 id="configuring-the-odoo-connection-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#configuring-the-odoo-connection-node"></a> Configuring the Odoo Connection Node</h3>
<p>Before you can send or receive any data from Odoo, FlowFuse needs to know how to connect to your Odoo instance. This is a one-time setup for your connection details, which can then be reused across all your Odoo nodes in FlowFuse.</p>
<ol>
<li>Drag any <code>odoo-xmlrpc</code> node (like <code>odoo-xmlrpc-read</code> or <code>odoo-xmlrpc-create</code>) onto your Node-RED canvas.</li>
<li>Double-click on the node to open its configuration.</li>
<li>Next to the "Host" field, click the pencil icon to add a new Odoo connection.</li>
<li>In the configuration dialog, you'll need to enter your Odoo instance's details:</li>
</ol>
<ul>
<li>Host URL: This is the web address of your Odoo instance (e.g., <code>https://databaseName.odoo.com</code>).</li>
<li>Database: The name of your Odoo database.</li>
<li>Username: Your Odoo login username (e.g., your email address).</li>
<li>Password: Your Odoo login password.</li>
</ul>
<ol start="5">
<li>Click "Add" to save this configuration.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/configuration-odoo-inuc464ll3-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/configuration-odoo-inuc464ll3-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Configuring Odoo Node" loading="lazy" decoding="async" src="https://flowfuse.com/img/configuration-odoo-inuc464ll3-650.jpeg" width="650" height="516" /></picture>
<em>Configuring Odoo Node</em></p>
<p>Now, any <code>odoo-xmlrpc</code> node you use can select this saved host configuration, meaning you only have to enter your credentials once.</p>
<p><em>Note: These configuration details (Host, Database, Username, Password) are confidential. To prevent exposing them when sharing your flows, it's crucial to use <strong>FlowFuse Environment Variables</strong>. These variables allow you to store sensitive information securely outside of your flow code. For more information, refer to our guide on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Environment Variables in Node-RED</a>.</em></p>
<h3 id="understanding-odoo-models" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#understanding-odoo-models"></a> Understanding Odoo Models</h3>
<p>Once your connection is set up, the next key concept for interacting with Odoo is understanding <strong>Models</strong>. In Odoo, a "model" represents a specific type of business object or data record, much like a table in a traditional database. Every piece of data you want to read, create, update, or delete belongs to a specific model.</p>
<p>Common Odoo models relevant to manufacturing include:</p>
<ul>
<li><code>product.template</code>: For general product information (e.g., product names, descriptions).</li>
<li><code>stock.quant</code>: For inventory quantities and locations.</li>
<li><code>mrp.production</code>: For manufacturing orders/production orders.</li>
<li><code>res.partner</code>: For contacts (customers, suppliers).</li>
<li><code>stock.picking</code>: For internal transfers or delivery orders.</li>
</ul>
<p>When you use an <code>odoo-xmlrpc</code> node in FlowFuse, you'll always need to specify which <code>model</code> you want to work with. If you're unsure of a specific model's name, you can often find it by enabling "Developer Mode" in your Odoo instance and hovering over fields in the Odoo interface.</p>
<p>Let’s get started. When explaining each operation, I will demonstrate it using different models such as <code>product.product</code> or <code>mrp.production</code>. This is just for demonstration and your understanding. You can perform these operations in the same way with other models—just make sure to pass the correct parameters according to the model and its data.</p>
<h3 id="reading-data-from-odoo" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#reading-data-from-odoo"></a> Reading Data from Odoo</h3>
<p>To read data from Odoo, you'll use the <code>odoo-xmlrpc-search_read</code> node. This node requires you to send the id (or ids) of the record(s) you wish to read to this node with <code>msg.payload</code>. The <code>msg.payload</code> should contain an array of the IDs you want to read.</p>
<p>Here is how you can read products data:</p>
<ol>
<li>Drag an inject node onto your canvas.</li>
<li>Connect it to a change node. Here, you'll set the query details:</li>
</ol>
<ul>
<li>Set <code>msg.payload</code> to the following array:</li>
</ul>
<div style="position: relative" id="code-container-186">
<pre class="language-json"><code id="code-186" class="language-json"><span class="token punctuation">[</span>ID<span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-186" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Replace ID with the actual product ID you want to read. You can include multiple IDs, for example:</p>
<div style="position: relative" id="code-container-190">
<pre class="language-json"><code id="code-190" class="language-json"><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-190" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Connect the change node to an odoo-xmlrpc-search_read node. Select your Odoo connection, enter model to <code>product.product</code> (or the Odoo model you want to query).</li>
<li>Connect to a debug node to view the data.</li>
<li>Deploy the flow and click the inject node button to see the result.</li>
</ol>
<p><lite-youtube videoid="9CdVOp_bDMk" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<div id="nr-flow-180" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow180 = "\n[{\"id\":\"4f2bd9814f07c6a6\",\"type\":\"odoo-xmlrpc-read\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"product.template\",\"x\":1190,\"y\":280,\"wires\":[[\"5601affdba752326\"]]},{\"id\":\"5601affdba752326\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"name\":\"debug 6\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1500,\"y\":280,\"wires\":[]},{\"id\":\"4e8b22877e33b496\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"name\":\"Read products with id 23 and 39\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":570,\"y\":280,\"wires\":[[\"b21cdd78ad81d65a\"]]},{\"id\":\"b21cdd78ad81d65a\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[39,23]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":280,\"wires\":[[\"4f2bd9814f07c6a6\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${HOST}\",\"db\":\"${DB_NAME}\",\"username\":\"${USERNAME} \",\"password\":\"${PASSWORD}\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow180.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-180') })</script>
<h3 id="creating-new-record-in-odoo" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#creating-new-record-in-odoo"></a> Creating New Record in Odoo</h3>
<p>To create new records in Odoo using FlowFuse, use the <code>odoo-xmlrpc-create</code> node. This node requires you to send an array of objects containing the record information as <code>msg.payload</code>. The array can include multiple objects, allowing you to create multiple records at once.</p>
<p>Here is how you can create manufacturing order:</p>
<ol>
<li>Drag an inject node onto your canvas and set it to trigger manually.</li>
<li>Connect it to a change node. Configure it to set <code>msg.payload</code> with the details for your new Odoo manufacturing order:</li>
</ol>
<ul>
<li>Set <code>msg.payload</code> to:</li>
</ul>
<div style="position: relative" id="code-container-240">
<pre class="language-json"><code id="code-240" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token property">"product_id"</span><span class="token operator">:</span> <span class="token number">39</span><span class="token punctuation">,</span> <br /> <span class="token property">"product_qty"</span><span class="token operator">:</span> <span class="token number">500</span><span class="token punctuation">,</span> <br /> <span class="token property">"product_uom_id"</span><span class="token operator">:</span> <span class="token number">1</span> <br /><span class="token punctuation">}</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-240" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the change node to an <code>odoo-xmlrpc-create</code> node. Select your configured Odoo connection for its Host and enter model to <code>mrp.production</code></li>
<li>Connect the <code>odoo-xmlrpc-create node</code> to a debug node to see the ID of the new record Odoo creates.</li>
</ol>
<p><lite-youtube videoid="bq_yaF8etmw" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<div id="nr-flow-181" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow181 = "\n[{\"id\":\"d89d98a5ec9a8733\",\"type\":\"odoo-xmlrpc-create\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"mrp.production\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":1200,\"y\":380,\"wires\":[[\"ea101f5cab65a241\"]]},{\"id\":\"99ba2ffda10cf2d9\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"name\":\"Create New MO\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":620,\"y\":380,\"wires\":[[\"33e0301b6e8f7838\"]]},{\"id\":\"ea101f5cab65a241\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"name\":\"debug 5\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1500,\"y\":380,\"wires\":[]},{\"id\":\"33e0301b6e8f7838\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[{\\\"product_id\\\":30,\\\"product_qty\\\":200,\\\"product_uom_id\\\":1}]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":380,\"wires\":[[\"d89d98a5ec9a8733\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${HOST}\",\"db\":\"${DB_NAME}\",\"username\":\"${USERNAME} \",\"password\":\"${PASSWORD}\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow181.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-181') })</script>
<h3 id="updating-existing-data-in-odoo" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#updating-existing-data-in-odoo"></a> Updating Existing Data in Odoo</h3>
<p>To modify existing records in Odoo using FlowFuse, you'll use the <code>odoo-xmlrpc-update</code> node. This node requires the ID of the record(s) you want to update and the new values for the fields you wish to change. The <code>msg.payload</code> should contain an array, where the first element is a list of record IDs and the second element is an object with the fields to update.</p>
<p>Here is how you can update the status of manufacturing order:</p>
<ol>
<li>Drag an inject node onto your canvas. Configure it to trigger manually.</li>
<li>Connect it to a change node. This node will prepare the <code>msg.payload</code> with the order ID and the new status.</li>
</ol>
<ul>
<li>Set <code>msg.payload</code> to :</li>
</ul>
<div style="position: relative" id="code-container-285">
<pre class="language-json"><code id="code-285" class="language-json"><span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><span class="token number">13</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token property">"state"</span><span class="token operator">:</span> <span class="token string">"progress"</span><span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-285" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the change node to an <code>odoo-xmlrpc-update</code> node. Select your configured Odoo connection for its Host and enter model to <code>mrp.production</code>.</li>
<li>Connect the <code>odoo-xmlrpc-update</code> node to a debug node to confirm the update operation. (A successful update typically returns true or an empty payload).</li>
<li>Deploy the flow and click the inject node button to see the result.</li>
</ol>
<p><lite-youtube videoid="SsPfHxCwMI8" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<div id="nr-flow-182" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow182 = "\n[{\"id\":\"e43dc05eb7df7ecf\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"name\":\"Update MO Status\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":610,\"y\":540,\"wires\":[[\"11bbd21f1314b839\"]]},{\"id\":\"47b01f56b4800c5c\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1500,\"y\":540,\"wires\":[]},{\"id\":\"bd9de404f2ac1a2e\",\"type\":\"odoo-xmlrpc-update\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"mrp.production\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":1200,\"y\":540,\"wires\":[[\"47b01f56b4800c5c\"]]},{\"id\":\"11bbd21f1314b839\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[ [18], {\\\"state\\\": \\\"progress\\\"} ]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":540,\"wires\":[[\"bd9de404f2ac1a2e\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${HOST}\",\"db\":\"${DB_NAME}\",\"username\":\"${USERNAME} \",\"password\":\"${PASSWORD}\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow182.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-182') })</script>
<h3 id="deleting-records-from-odoo-(unlink)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#deleting-records-from-odoo-(unlink)"></a> Deleting Records from Odoo (Unlink)</h3>
<p>To delete records in Odoo using FlowFuse, you'll use the <code>odoo-xmlrpc-unlink</code> node. This node requires you to send the id (or ids) of the record(s) you wish to remove. The <code>msg.payload</code> should contain an array of the IDs you want to delete.</p>
<p>Here is how you can delete product from inventory:</p>
<ol>
<li>Drag an inject node onto your canvas. Configure it to trigger manually.</li>
<li>Connect the inject node to a change node. This node will prepare the msg.payload with the ID(s) of the record(s) to delete.</li>
</ol>
<ul>
<li>Set <code>msg.payload</code> to a JSON array containing the ID of the manufacturing order to delete:</li>
</ul>
<div style="position: relative" id="code-container-335">
<pre class="language-json"><code id="code-335" class="language-json"><span class="token punctuation">[</span><span class="token number">16</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-335" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the change node to an <code>odoo-xmlrpc-unlink</code>. Select your configured Odoo connection for its Host and enter model to <code>mrp.production</code>.</li>
<li>Connect the <code>odoo-xmlrpc-unlink</code> to a debug node to confirm the unlink operation.</li>
<li>Deploy the flow and click the inject node button to see the result.</li>
</ol>
<p><lite-youtube videoid="1O1JYRtX-Sg" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<div id="nr-flow-183" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow183 = "\n[{\"id\":\"f14241bb24af8dc8\",\"type\":\"odoo-xmlrpc-unlink\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"product.template\",\"x\":1190,\"y\":700,\"wires\":[[\"231e32e70753ab22\"]]},{\"id\":\"9452d125fd059f79\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"name\":\"Delete the product with ID 60\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":580,\"y\":700,\"wires\":[[\"cfcabeae8b291a9c\"]]},{\"id\":\"231e32e70753ab22\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"name\":\"debug 7\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1500,\"y\":700,\"wires\":[]},{\"id\":\"cfcabeae8b291a9c\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[60]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":700,\"wires\":[[\"f14241bb24af8dc8\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${HOST}\",\"db\":\"${DB_NAME}\",\"username\":\"${USERNAME} \",\"password\":\"${PASSWORD}\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow183.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-183') })</script>
<h3 id="advanced-search-with-filters-and-fields" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#advanced-search-with-filters-and-fields"></a> Advanced Search with Filters and Fields</h3>
<p>For advanced queries, you'll use the <code>odoo-xmlrpc-search_read</code> node. This versatile node combines the ability to search for records using complex criteria (filters) and to retrieve only the specific data fields you need from those results.</p>
<h4 id="understanding-filters" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#understanding-filters"></a> Understanding Filters</h4>
<p>Filters are conditions you apply to narrow down your search results. They are structured as a <strong>list of lists</strong>, where each inner list defines a single condition: <code>[field, operator, value]</code>.</p>
<ul>
<li><code>field</code>: The name of the Odoo field you want to filter by (e.g., <code>qty_available</code>, <code>state</code>, <code>name</code>).</li>
<li><code>operator</code>: How you want to compare the field. Common operators include:
<ul>
<li><code>=</code>: Equal to</li>
<li><code>!=</code>: Not equal to</li>
<li><code>></code>: Greater than</li>
<li><code><</code>: Less than</li>
<li><code>>=</code>: Greater than or equal to</li>
<li><code><=</code>: Less than or equal to</li>
<li><code>in</code>: Value is in a list (e.g., <code>[["id", "in", [1, 2, 3]]</code>)</li>
<li><code>not in</code>: Value is not in a list</li>
<li><code>ilike</code>: Case-insensitive "like" (contains substring)</li>
<li><code>=like</code>: Case-sensitive "like"</li>
</ul>
</li>
<li><code>value</code>: The value you're comparing against.</li>
</ul>
<p>When you include multiple conditions within your filters list, Odoo treats them as an "AND" relationship by default. This means all conditions must be true for a record to be returned.</p>
<p><strong>Example Filters:</strong></p>
<ul>
<li><code>[[["list_price", "<", 10]]]</code>: Find products with less than 10 units in stock.</li>
<li><code>[[["state", "=", "progress"], ["product_id", "=", 38]]]</code>: Find manufacturing orders for a specific product that are currently "in progress."</li>
<li><code>[[["name", "ilike", "coating"]]]</code>: Find products where the name contains "coating" (case-insensitive).</li>
</ul>
<h4 id="understanding-fields" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#understanding-fields"></a> Understanding Fields</h4>
<p>The <code>fields</code> parameter allows you to specify exactly which columns or properties you want to retrieve for the matching records. This is important for efficiency, as it avoids pulling unnecessary data, making your flows faster and your payloads smaller.</p>
<ul>
<li><strong>Structure:</strong> A simple list of field names (e.g., <code>["name", "qty_available", "default_code"]</code>).</li>
</ul>
<h4 id="controlling-results%3A-offset-and-limit" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#controlling-results%3A-offset-and-limit"></a> Controlling Results: Offset and Limit</h4>
<p>For larger datasets, you'll want to control how many records are returned and where the results start.</p>
<ul>
<li>offset: This parameter specifies the number of records to skip from the beginning of the result set. It's useful for pagination.</li>
<li>limit: This parameter specifies the maximum number of records to return in a single query. It's crucial for managing the amount of data you retrieve.</li>
</ul>
<h4 id="example-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#example-flow"></a> Example Flow</h4>
<p>Here’s an example FlowFuse flow to find products with list price (more than 1000) that are also marked as "saleable" in Odoo, retrieving only their name, quantity, internal reference, and list price, and limiting the results.</p>
<ol>
<li>Drag an inject node onto your canvas and configure it to trigger manually.</li>
<li>Connect it to a change node. This node will define your search criteria (filters and fields) and also set the offset and limit.</li>
</ol>
<ul>
<li>Set <code>msg.filters</code> to JSON:</li>
</ul>
<div style="position: relative" id="code-container-517">
<pre class="language-json"><code id="code-517" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><span class="token string">"list_price"</span><span class="token punctuation">,</span> <span class="token string">">"</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"sale_ok"</span><span class="token punctuation">,</span> <span class="token string">"="</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">]</span><br /><span class="token punctuation">]</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-517" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ul>
<li>Set <code>msg.fields</code> to JSON:</li>
</ul>
<div style="position: relative" id="code-container-525">
<pre class="language-json"><code id="code-525" class="language-json"><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">,</span> <span class="token string">"qty_available"</span><span class="token punctuation">,</span> <span class="token string">"default_code"</span><span class="token punctuation">,</span> <span class="token string">"standard_price"</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-525" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ul>
<li>Set <code>msg.offset</code> to Number 0 (Start from the first record).</li>
<li>Set <code>msg.limit</code> to Number 5 (Retrieve a maximum of 10 records).</li>
</ul>
<ol start="3">
<li>Connect the change node to an <code>odoo-xmlrpc-search_read</code> node. Select your configured Odoo connection.</li>
<li>Connect to a debug node to inspect the filtered and selected data in the debug sidebar.</li>
<li>Deploy the flow and click the inject node button to see the result.</li>
</ol>
<p><lite-youtube videoid="8Asa3z2VctQ" params="rel=0" style="margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;" title="YouTube video player"></lite-youtube></p>
<div id="nr-flow-184" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow184 = "\n[{\"id\":\"0cfe26fd5b4169e7\",\"type\":\"debug\",\"z\":\"295d40790bd21f48\",\"name\":\"debug 4\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1500,\"y\":840,\"wires\":[]},{\"id\":\"656023449ba5dee9\",\"type\":\"inject\",\"z\":\"295d40790bd21f48\",\"name\":\"Read Top 5 Saleable Products >1000\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":550,\"y\":840,\"wires\":[[\"7f791bfc8caa0721\"]]},{\"id\":\"8ada5a972d94dd9d\",\"type\":\"odoo-xmlrpc-search-read\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"host\":\"18818bdefd1f27ce\",\"model\":\"product.product\",\"filter\":\"\",\"offset\":0,\"limit\":100,\"x\":1210,\"y\":840,\"wires\":[[\"0cfe26fd5b4169e7\"]]},{\"id\":\"7f791bfc8caa0721\",\"type\":\"change\",\"z\":\"295d40790bd21f48\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"filters\",\"pt\":\"msg\",\"to\":\"[[[\\\"list_price\\\",\\\">\\\",1000],[\\\"sale_ok\\\",\\\"=\\\",true]]]\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"limit\",\"pt\":\"msg\",\"to\":\"5\",\"tot\":\"num\"},{\"t\":\"set\",\"p\":\"offset\",\"pt\":\"msg\",\"to\":\"0\",\"tot\":\"num\"},{\"t\":\"set\",\"p\":\"fields\",\"pt\":\"msg\",\"to\":\"[\\\"name\\\",\\\"qty_available\\\",\\\"default_code\\\",\\\"lst_price\\\"]\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":900,\"y\":840,\"wires\":[[\"8ada5a972d94dd9d\"]]},{\"id\":\"18818bdefd1f27ce\",\"type\":\"odoo-xmlrpc-config\",\"url\":\"${HOST}\",\"db\":\"${DB_NAME}\",\"username\":\"${USERNAME} \",\"password\":\"${PASSWORD}\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow184.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-184') })</script>
<h2 id="final-thought" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/06/connect-shop-floor-to-odoo-erp-flowfuse/#final-thought"></a> Final thought</h2>
<p>So, we've talked about how those manual data methods—paper, spreadsheets—can really slow things down, cause mistakes, and cost money in your factory. And let's be honest, many digital fixes out there just add more complexity or demand specialized coding skills that your team might not have.</p>
<p>This is where FlowFuse comes in. FlowFuse is a platform made for factory floors that runs on your devices right there. It connects to all your machines, old or new, and even your ERP systems like Odoo. FlowFuse collects, cleans, and moves your data. It helps your engineers, who know your operations best, build industrial applications and solutions using simple drag-and-drop actions. This means they can link your factory data directly to your ERP, getting rid of all those manual steps and saving your IT team time and money.</p>
<p>What's the real payoff? You get to see less wasted time and money, fewer mistakes with accurate, real-time data, and simply better control over your whole factory. FlowFuse helps your entire operation run smarter and more reliably. If you're looking for a practical way to bring these kinds of improvements to your own manufacturing processes, we'd be glad to discuss how FlowFuse can assist. <a href="https://flowfuse.com/contact-us/">Get in touch here</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/Designing Flexible Cron Schedules in FlowFuse with Node-REDGo Beyond Inject Nodes: Automate Smarter with Flexible Cron Schedules in Node-RED2025-05-15T00:00:00ZSumit ShindeSteve McLaughlin<p>Automation isn’t just about reacting to events—sometimes it’s about doing things at the right time. In Node-RED, the Inject node is great for triggering flows at set intervals, but it’s limited when you need more control. Cron jobs offer precise scheduling, letting you set up custom times for your tasks. In this guide, we'll show you how to create flexible cron schedules in FlowFuse with Node-RED, so your flows run exactly when needed.</p>
<!--more-->
<h2 id="what-is-a-cron-job%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#what-is-a-cron-job%3F"></a> What is a Cron Job?</h2>
<p>Let’s kick things off by demystifying what a cron job actually is. You’ve probably heard the term before, and while it might sound complex, it’s really just a way of setting up tasks to run at specific times — automatically.</p>
<p>Think of it this way: with Node-RED’s Inject node, you can trigger tasks at intervals like every 5 seconds, every minute, or even on specific weekdays at set times (for example, every Monday, Tuesday, or Sunday). But when you use cron jobs, you gain much more control over the timing.</p>
<p>For example, you can trigger a task every two hours, only on weekdays, but skip holidays or run a job every 5 minutes during business hours, but only in the first week of each quarter. You can even schedule flows to run at 6:45 AM on the last Friday of every month, or at 11:59 PM on the last day of the year — these kinds of patterns are either extremely complex or completely unachievable using just the Inject node.</p>
<p>The magic of cron lies in its ability to express complex time logic in a simple, compact format — perfect for orchestrating automation schedules that go well beyond what the basic Inject node can offer.</p>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#prerequisites"></a> Prerequisites</h2>
<p>Before we start building flexible cron schedules in FlowFuse, make sure you have the following in place:</p>
<ul>
<li><strong>Running FlowFuse Instance:</strong> Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out our <a href="https://app.flowfuse.com/account/create">free trial</a> and learn how to create an instance in FlowFuse.</li>
<li><strong>node-red-contrib-cron-plus:</strong> Ensure you have <a href="https://flows.nodered.org/node/node-red-contrib-cron-plus">node-red-contrib-cron-plus</a> installed, It’s developed by Steve, a software engineer here at FlowFuse and one of the core maintainers of Node-RED.</li>
</ul>
<h2 id="building-scheduled-automations-with-cron-plus" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#building-scheduled-automations-with-cron-plus"></a> Building Scheduled Automations with cron-plus</h2>
<p>Now that you understand what cron jobs are and why they’re useful, let’s dive into building them inside Node-RED using the cron-plus node. When working with cron-plus, you’ll encounter different types of schedules—each suited for different needs—and varying levels of complexity depending on what you're trying to automate.</p>
<p>At the most basic level, you can define static schedules using familiar cron expressions (like "every 5 minutes" or "at 8:00 AM daily"). As you progress, you’ll learn to use solar event triggers (like sunrise or sunset), create date-specific schedules, and even manage schedules dynamically at runtime—adding, removing, or modifying them based on incoming data or user interactions.</p>
<p>In this section, we’ll walk through each of these layers step by step, starting with the simplest use cases and gradually moving into more powerful and flexible scheduling techniques—giving you full control over when and how your flows run.</p>
<blockquote>
<p>Tip: This article draws information from the node’s <a href="https://flows.nodered.org/node/node-red-contrib-cron-plus">README</a>, which is highly informative. I recommend going through it as well for more details.</p>
</blockquote>
<h3 id="static-schedules" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#static-schedules"></a> Static Schedules</h3>
<p>The most straightforward way to use cron-plus is to define static schedules using cron expressions. These are pre-configured inside the node and run on a fixed pattern—perfect for predictable, repetitive tasks. For example we need to Trigger a flow every day at 8:00 AM.</p>
<ol>
<li>
<p>Drag and drop the cron-plus node onto your Node-RED workspace.</p>
</li>
<li>
<p>Double-click on the cron-plus node to open its configuration panel.</p>
</li>
<li>
<p>If no schedules are configured yet, click the +add button to add a new schedule. Enter the following:</p>
<ul>
<li>Schedule Name: e.g., "Daily 8 AM"</li>
<li>Topic: Enter a topic to send with the message when triggered, such as "daily-trigger".</li>
<li>Payload: Choose what payload you want to send when the cron job triggers, such as "Triggering Flow at 8 AM".</li>
</ul>
</li>
<li>
<p>Select Cron from the Schedule Type dropdown.</p>
</li>
<li>
<p>In the Schedule field, enter the following cron expression to run the task at 8:00 AM every day:</p>
</li>
</ol>
<pre><code> 0 8 * * *
</code></pre>
<p>A cron expression is composed of five fields (sometimes six or seven, depending on the system) that determine the schedule for executing tasks.
Below is a breakdown of each field that node-red-contrib-cron-plus supports:</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Possible Values</th>
<th>Special Symbols</th>
</tr>
</thead>
<tbody>
<tr>
<td>Second (optional)</td>
<td><code>0-59</code></td>
<td><code>* / , -</code></td>
</tr>
<tr>
<td>Minute</td>
<td><code>0-59</code></td>
<td><code>* / , -</code></td>
</tr>
<tr>
<td>Hour</td>
<td><code>0-23</code></td>
<td><code>* / , -</code></td>
</tr>
<tr>
<td>Day of Month</td>
<td><code>1-31</code></td>
<td><code>* / , - ? L W</code></td>
</tr>
<tr>
<td>Month</td>
<td><code>1-12</code> or <code>JAN-DEC</code></td>
<td><code>* / , -</code></td>
</tr>
<tr>
<td>Day of Week</td>
<td><code>0-6</code> or <code>SUN-SAT</code></td>
<td><code>* / , - ? L #</code></td>
</tr>
<tr>
<td>Year (optional)</td>
<td><code>1970-2099</code></td>
<td><code>* / , -</code></td>
</tr>
</tbody>
</table>
<p>Here are some examples of how you can use the special symbols and shorthand's:</p>
<table>
<thead>
<tr>
<th>Symbol</th>
<th>Meaning</th>
<th>Example</th>
<th>Explanation</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>*</code></td>
<td>All possible values</td>
<td><code>* * * * *</code></td>
<td>Every minute of every hour, day, month, and weekday</td>
</tr>
<tr>
<td><code>?</code></td>
<td>No specific value</td>
<td><code>0 0 12 ? * MON</code></td>
<td>At 12 PM Only on Mondays (no specific day of the month)</td>
</tr>
<tr>
<td><code>-</code></td>
<td>Range</td>
<td><code>0 10-12 * * * *</code></td>
<td>Minutes 10, 11, and 12 of every hour</td>
</tr>
<tr>
<td><code>,</code></td>
<td>List of values</td>
<td><code>0 0 12 * 1,3,5 *</code></td>
<td>At 12 PM only in January, March, and May</td>
</tr>
<tr>
<td><code>/</code></td>
<td>Step values</td>
<td><code>*/15 * * * *</code></td>
<td>Every 15 minutes (00, 15, 30, 45)</td>
</tr>
<tr>
<td><code>L</code></td>
<td>Last</td>
<td><code>0 0 12 L * *</code></td>
<td>12 PM on the last day of the month</td>
</tr>
<tr>
<td><code>W</code></td>
<td>Nearest weekday</td>
<td><code>0 0 0 15W * * *</code></td>
<td>At midnight on the nearest weekday to the 15th of the month</td>
</tr>
<tr>
<td><code>#</code></td>
<td>nth weekday of the month</td>
<td><code>0 0 0 * * MON#1 *</code></td>
<td>At midnight on the first Monday of the month</td>
</tr>
</tbody>
</table>
<ol start="6">
<li>
<p>Connect the cron-plus node to other nodes (e.g., a debug node or an action node) to specify the actions when the flow is triggered.</p>
</li>
<li>
<p>Click Deploy to save and activate your flow. The cron-plus node will now trigger your flow every day at 8:00 AM.</p>
</li>
</ol>
<div id="nr-flow-171" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow171 = "\n[{\"id\":\"cba15bd32c5434a5\",\"type\":\"group\",\"z\":\"b37428694e90b2c5\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"2af2e9274fe1321a\",\"458c9533a1437ee1\"],\"x\":94,\"y\":179,\"w\":392,\"h\":82},{\"id\":\"2af2e9274fe1321a\",\"type\":\"debug\",\"z\":\"b37428694e90b2c5\",\"g\":\"cba15bd32c5434a5\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":380,\"y\":220,\"wires\":[]},{\"id\":\"458c9533a1437ee1\",\"type\":\"cronplus\",\"z\":\"b37428694e90b2c5\",\"g\":\"cba15bd32c5434a5\",\"name\":\"Cron Plus\",\"outputField\":\"payload\",\"timeZone\":\"\",\"storeName\":\"\",\"commandResponseMsgOutput\":\"output1\",\"defaultLocation\":\"\",\"defaultLocationType\":\"default\",\"outputs\":1,\"options\":[{\"name\":\"Daily 8 AM\",\"topic\":\"daily-trigger\",\"payloadType\":\"default\",\"payload\":\"\",\"expressionType\":\"cron\",\"expression\":\" 0 8 * * *\",\"location\":\"\",\"offset\":\"0\",\"solarType\":\"all\",\"solarEvents\":\"sunrise,sunset\"}],\"x\":200,\"y\":220,\"wires\":[[\"2af2e9274fe1321a\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow171.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-171') })</script>
<p>Here are some advance patterns:</p>
<p><strong>Every weekday at 9:30 AM</strong></p>
<pre><code>30 9 * * 1-5
</code></pre>
<p><strong>Every hour</strong></p>
<pre><code>0 * * * *
</code></pre>
<p><strong>Every 15 min during work hours (Mon–Fri, 9–5)</strong></p>
<pre><code>*/15 9-16 * * 1-5
</code></pre>
<p><strong>First Monday of the month at 10:00 AM</strong></p>
<pre><code>0 10 * * MON#1
</code></pre>
<p><strong>Last Friday of the month at 6:45 AM</strong></p>
<pre><code>45 6 ? * FRIL
</code></pre>
<h4 id="easy-builder-feature" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#easy-builder-feature"></a> Easy Builder Feature</h4>
<p>To make it even easier to create and customize cron patterns, the cron-plus node includes a feature called Easy Builder. This feature provides a user-friendly interface that lets you quickly generate and adjust cron expressions without needing to write them manually. You can select options like time intervals, days of the week, and more, and the Easy Builder will generate the correct cron syntax for you.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/easy-builder-0-X2nUUks5-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the easy builder feature" loading="lazy" decoding="async" src="https://flowfuse.com/img/easy-builder-0-X2nUUks5-800.webp" width="800" height="795" /></picture>
<em>Image showing the easy builder feature</em></p>
<h3 id="solar-event-schedules" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#solar-event-schedules"></a> Solar Event Schedules</h3>
<p>Solar event-based triggers are a great feature in cron-plus for automating tasks based on sunlight events like sunrise, sunset, dawn, and dusk. You can fine-tune your triggers with offsets, like triggering an action 30 minutes after sunset.</p>
<p>Here are the solar events you can use:</p>
<ul>
<li><strong>nightEnd</strong>: End of night, start of twilight.</li>
<li><strong>nauticalDawn</strong>: Horizon becomes faintly visible, used by sailors.</li>
<li><strong>civilDawn</strong>: Light enough for outdoor activities without lights.</li>
<li><strong>sunrise</strong>: Sun first visible on the horizon.</li>
<li><strong>sunriseEnd</strong>: Full sun above the horizon.</li>
<li><strong>morningGoldenHourEnd</strong>: End of the morning golden hour.</li>
<li><strong>solarNoon</strong>: Sun is at its highest point in the sky.</li>
<li><strong>eveningGoldenHourStart</strong>: Start of the evening golden hour.</li>
<li><strong>sunsetStart</strong>: Sun starts to set.</li>
<li><strong>sunset</strong>: Sun is fully below the horizon.</li>
<li><strong>civilDusk</strong>: Last light before it gets dark.</li>
<li><strong>nauticalDusk</strong>: Horizon is no longer visible, dark sky.</li>
<li><strong>nightStart</strong>: Full darkness after twilight.</li>
<li><strong>nadir</strong>: Darkest point of the night.</li>
</ul>
<p>Let's start and learn how to use it. Suppose we need to trigger the flow 30 minutes after sunset. Here's how you do it:</p>
<p>Let’s start by setting up a flow that triggers 30 minutes after sunset. Here’s how:</p>
<ol>
<li>
<p>Drag a new <code>cron-plus</code> node onto your Node-RED canvas.</p>
</li>
<li>
<p>Set the location — solar events depend on your geographic location. You can configure this at the node level or per schedule. In the Location field, either:</p>
<ul>
<li>Enter your latitude and longitude manually, or</li>
<li>Click the three-dot icon to open a map and select your location.</li>
</ul>
</li>
<li>
<p>Add a new schedule. Enter the following Schedule Name, Topic, Payload</p>
</li>
<li>
<p>Choose the solar event — set the Schedule Type to <code>solar</code>.<br />
You can choose "All Solar Events" or "Selected Solar Events" to pick specific ones.<br />
For this example, choose "sunset".</p>
</li>
<li>
<p>Set the offset — this defines how much earlier or later the trigger should happen compared to the solar event.<br />
For 30 minutes after sunset, enter:</p>
<pre><code>30
</code></pre>
<p>You can also use negative numbers (like <code>-10</code> for 10 minutes before sunset) or larger values like <code>60</code> for 1 hour after.</p>
</li>
<li>
<p>Click Done, connect the node to the rest of your flow, and Deploy.</p>
</li>
</ol>
<div id="nr-flow-172" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow172 = "\n[{\"id\":\"cba15bd32c5434a5\",\"type\":\"group\",\"z\":\"b37428694e90b2c5\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"2af2e9274fe1321a\",\"458c9533a1437ee1\"],\"x\":94,\"y\":179,\"w\":392,\"h\":82},{\"id\":\"2af2e9274fe1321a\",\"type\":\"debug\",\"z\":\"b37428694e90b2c5\",\"g\":\"cba15bd32c5434a5\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":380,\"y\":220,\"wires\":[]},{\"id\":\"458c9533a1437ee1\",\"type\":\"cronplus\",\"z\":\"b37428694e90b2c5\",\"g\":\"cba15bd32c5434a5\",\"name\":\"Cron Plus\",\"outputField\":\"payload\",\"timeZone\":\"\",\"storeName\":\"\",\"commandResponseMsgOutput\":\"output1\",\"defaultLocation\":\"\",\"defaultLocationType\":\"default\",\"outputs\":1,\"options\":[{\"name\":\"et\",\"topic\":\"30min After Sunset\",\"payloadType\":\"default\",\"payload\":\"\",\"expressionType\":\"solar\",\"expression\":\" 0 8 * * *\",\"location\":\"20.70816594524601 75.673828125\",\"offset\":\"30\",\"solarType\":\"selected\",\"solarEvents\":\"sunset\"}],\"x\":200,\"y\":220,\"wires\":[[\"2af2e9274fe1321a\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow172.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-172') })</script>
<p>Your flow will now trigger 30 minutes after sunset every day — automatically adjusting for seasonal changes based on your location.</p>
<h3 id="date-sequence-schedules" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#date-sequence-schedules"></a> Date Sequence Schedules</h3>
<p>While cron expressions and solar events are great for recurring patterns, sometimes you need to schedule flows to run at very specific, one-time moments—like a product launch, system maintenance window, or a holiday-specific action. This is where the Date Sequence schedule type in cron-plus comes in.</p>
<p>With this method, you can define exact dates and times (including timezone support), and the node will trigger your flow at those precise moments—once or multiple times, depending on the list you define.</p>
<p>The sequence field supports:</p>
<ul>
<li>UNIX timestamps (in milliseconds)</li>
</ul>
<pre><code>1767225600000
</code></pre>
<ul>
<li>Date and time (in plain text)</li>
</ul>
<pre><code>2026-04-03 00:00
</code></pre>
<ul>
<li>Date and time with timezone</li>
</ul>
<pre><code>2026-04-06 12:00 GMT+0
</code></pre>
<p>You can list multiple times, separated by commas:</p>
<pre><code>1767225600000, 2026-04-03 00:00, 2026-04-06 12:00 GMT+0
</code></pre>
<p>Let’s learn how to set this up:</p>
<ol>
<li>Drag a new cron-plus node onto the canvas.</li>
<li>Double-click the node to open its settings.</li>
<li>Click +add to create a new schedule.</li>
<li>Give the schedule a name and configure the topic — this is the value that will be sent with the message when the schedule triggers.</li>
<li>Set the payload to whatever message or data you want to send when triggered.</li>
<li>Choose Date Sequence from the Schedule Type dropdown.</li>
<li>In the Expression field, enter one or more dates using any of the 8. supported formats we discussed above (UNIX timestamp, plain date/time, or date/time with timezone). You can separate multiple values with commas.</li>
<li>Click Done, then connect the cron-plus node to the rest of your flow (e.g., a debug node to see it trigger, or an action node to perform something).</li>
<li>Finally, click Deploy to save and start your schedule.</li>
</ol>
<div id="nr-flow-173" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow173 = "\n[{\"id\":\"2ff341cec3114c53\",\"type\":\"cronplus\",\"z\":\"b37428694e90b2c5\",\"g\":\"2aa2d59a36bf0fa7\",\"name\":\"Date Sequence\",\"outputField\":\"payload\",\"timeZone\":\"\",\"storeName\":\"\",\"commandResponseMsgOutput\":\"output1\",\"defaultLocation\":\"\",\"defaultLocationType\":\"default\",\"outputs\":1,\"options\":[{\"name\":\"schedule1\",\"topic\":\"fixed dates\",\"payloadType\":\"str\",\"payload\":\"fixed\",\"expressionType\":\"dates\",\"expression\":\"1767225600000, 2026-04-03 00:00, 2026-04-06 12:00 GMT+0\",\"location\":\"\",\"offset\":\"0\",\"solarType\":\"all\",\"solarEvents\":\"sunrise,sunset\"}],\"x\":300,\"y\":780,\"wires\":[[\"b66ebe91df84b4ce\"]]},{\"id\":\"b66ebe91df84b4ce\",\"type\":\"debug\",\"z\":\"b37428694e90b2c5\",\"g\":\"2aa2d59a36bf0fa7\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":560,\"y\":780,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow173.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-173') })</script>
<p>The flow will now trigger exactly at each date and time you've specified.</p>
<h3 id="handling-time-zones-and-daylight-saving-time-(dst)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#handling-time-zones-and-daylight-saving-time-(dst)"></a> Handling Time Zones and Daylight Saving Time (DST)</h3>
<p>When dealing with scheduled tasks, managing time zones and Daylight Saving Time (DST) is crucial to ensure your cron jobs trigger at the correct local time. With cron-plus in Node-RED, this is made easy. Cron-plus provides global time zone support, allowing you to specify the time zone for all schedules within a particular cron-plus node. When configuring the cron-plus node, you'll see an input field labeled "Timezone", where you can start typing your desired time zone and select from suggested options.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/timezone-selection-P3oTP_a0k4-1068.avif 1068w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/timezone-selection-P3oTP_a0k4-1068.webp 1068w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the Timezone input field in the cron-plus node configuration with auto-suggestions while typing" loading="lazy" decoding="async" src="https://flowfuse.com/img/timezone-selection-P3oTP_a0k4-1068.jpeg" width="1068" height="964" /></picture>
<em>Screenshot showing the Timezone input field in the cron-plus node configuration with auto-suggestions while typing</em></p>
<p>For example, if your tasks need to run in the Eastern Time Zone, you would enter "America/New_York" in the Timezone field. Similarly, for the UK time zone, you would enter "Europe/London".</p>
<p>When scheduling tasks in a time zone that observes Daylight Saving Time (DST), cron-plus will automatically adjust your task’s execution times to account for the DST changes. If you set a schedule for 8:00 AM daily, cron-plus will ensure that the task triggers at 8:00 AM local time, whether it's during Standard Time (e.g., EST) or Daylight Saving Time (e.g., EDT).</p>
<p>When DST begins (e.g., in spring), cron-plus will shift the scheduled task forward by one hour. Similarly, when DST ends (e.g., in fall), the task will shift back by one hour. This automatic adjustment is handled based on the time zone you enter.</p>
<h3 id="understanding-node-status-symbols-and-their-descriptions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#understanding-node-status-symbols-and-their-descriptions"></a> Understanding Node Status Symbols and Their Descriptions</h3>
<p>The cron-plus node visually indicates its state using status markers in the flow editor:</p>
<ul>
<li><strong>● (Dot):</strong> Indicates a static schedule configured directly in the node.</li>
<li><strong>○ (Ring):</strong> Indicates a dynamic schedule added via input messages.</li>
</ul>
<p>Also, the node indicates the next event, along with the type of schedule. For example, node in the following image specifies that the next event is on May 26, 2025, at 12:00 AM GMT +5:30. It is a static schedule, and its name is 'Schedule1'</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-status-event-UWIDeFQGce-500.avif 500w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-status-event-UWIDeFQGce-500.webp 500w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A node showing the next event scheduled for May 26, 2025, at 12:00 AM GMT +5:30 with a static schedule named 'Schedule1'." loading="lazy" decoding="async" src="https://flowfuse.com/img/node-status-event-UWIDeFQGce-500.jpeg" width="500" height="106" /></picture>
<em>A node showing the next event scheduled for May 26, 2025, at 12:00 AM GMT +5:30 with a static schedule named 'Schedule1'.</em></p>
<h3 id="dynamic-schedule-control-via-input-messages" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#dynamic-schedule-control-via-input-messages"></a> Dynamic Schedule Control via Input Messages</h3>
<p>In some cases, you might not know your schedule ahead of time—or you may want it to change based on user actions or incoming data. The cron-plus node supports this with dynamic control using input messages. This means you can add, update, or remove schedules while your flow is running, without opening the editor. It’s a powerful way to make your automations more responsive and adaptable.</p>
<p>Each control message is sent to the <code>cron-plus</code> node using a specially formatted <code>msg.payload</code> with a command and associated configuration.</p>
<p>Below are some examples of dynamic commands. Please refer to the built-in help of the cron-plus node for more details:</p>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>trigger</code></td>
<td>Triggers a schedule by name.</td>
<td><code>{ "command": "trigger", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>add</code></td>
<td>Add (or update) a dynamic schedule.</td>
<td><code>{ "command": "add", "name": "dynamic-1", "topic": "dynamic-schedule", "payloadType": "default", "expressionType": "cron", "expression": "*/2 * * * *" }</code></td>
</tr>
<tr>
<td><code>remove</code></td>
<td>Removes a specific schedule by name.</td>
<td><code>{ "command": "remove", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>start</code></td>
<td>Starts a specific schedule by name.</td>
<td><code>{ "command": "start", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>stop</code></td>
<td>Stops a specific schedule by name.</td>
<td><code>{ "command": "stop", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>pause</code></td>
<td>Pauses a specific schedule by name.</td>
<td><code>{ "command": "pause", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>export</code></td>
<td>Exports a schedule by name.</td>
<td><code>{ "command": "export", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>status</code></td>
<td>Provides the status of a specific schedule by name.</td>
<td><code>{ "command": "status", "name": "dynamic-1" }</code></td>
</tr>
<tr>
<td><code>describe</code></td>
<td>Provides a human-readable description of a cron or solar expression.</td>
<td><code>{ "command": "describe", "expression": "0 8 * * 1-5", "expressionType": "cron" }</code></td>
</tr>
</tbody>
</table>
<p>Commands can also include a <code>filter</code> to operate on multiple schedules at once. Below are a few examples. Please refer to the built-in help of the cron-plus node for more details:</p>
<table>
<thead>
<tr>
<th>Filter</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-all</code></td>
<td>Operate a command on all schedules.</td>
<td><code>{ "command": "start-all" }</code></td>
</tr>
<tr>
<td><code>-all-dynamic</code></td>
<td>Operate a command on all dynamic schedules.</td>
<td><code>{ "command": "export-all-dynamic" }</code></td>
</tr>
<tr>
<td><code>-all-static</code></td>
<td>Operate a command on all static schedules.</td>
<td><code>{ "command": "pause-all-static" }</code></td>
</tr>
<tr>
<td><code>-all-active</code></td>
<td>Operate a command on all active schedules.</td>
<td><code>{ "command": "stop-all-active" }</code></td>
</tr>
<tr>
<td><code>-all-inactive</code></td>
<td>Operate a command on all inactive schedules.</td>
<td><code>{ "command": "start-all-inactive" }</code></td>
</tr>
<tr>
<td><code>-all-active-static</code></td>
<td>Operate a command on all active static schedules.</td>
<td><code>{ "command": "stop-all-active-static" }</code></td>
</tr>
<tr>
<td><code>-all-active-dynamic</code></td>
<td>Operate a command on all active dynamic schedules.</td>
<td><code>{ "command": "stop-all-active-dynamic" }</code></td>
</tr>
<tr>
<td><code>-all-inactive-static</code></td>
<td>Operate a command on all inactive static schedules.</td>
<td><code>{ "command": "start-all-inactive-static" }</code></td>
</tr>
<tr>
<td><code>-all-inactive-dynamic</code></td>
<td>Operate a command on all inactive dynamic schedules.</td>
<td><code>{ "command": "remove-all-inactive-dynamic" }</code></td>
</tr>
</tbody>
</table>
<h4 id="dynamic-demo-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#dynamic-demo-1"></a> Dynamic Demo 1</h4>
<p>For example, we need to build a flow that triggers on UK public holidays to stop recording the OEE (Overall Equipment Efficiency) or lock the entry gates of the factory. To achieve this, we can integrate a UK public holiday API into our Node-RED flow, fetch the holiday data, and then trigger actions based on those holidays.</p>
<ol>
<li>Drag the Inject node onto the canvas and set it to trigger on deploy after <code>0.1</code> seconds.</li>
<li>Next, drag the HTTP request node onto the canvas, set the method to GET, and use the URL <code>https://www.gov.uk/bank-holidays.json</code>. Set the return to "parsed JSON object".</li>
<li>Drag the Function node onto the canvas and add the following JavaScript code into it:</li>
</ol>
<div style="position: relative" id="code-container-927">
<pre class="language-javascript"><code id="code-927" class="language-javascript"><span class="token comment">// Retrieve public holidays for England and Wales from the API response</span><br /><span class="token keyword">const</span> engHols <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">[</span><span class="token string">"england-and-wales"</span><span class="token punctuation">]</span><span class="token punctuation">.</span>events<span class="token punctuation">;</span><br /><br /><span class="token comment">// Clear out any existing schedules before adding new ones</span><br />node<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">topic</span><span class="token operator">:</span> <span class="token string">'remove-all'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Create an array to hold the new holiday schedules</span><br /><span class="token keyword">const</span> newSchedules <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Loop through all the holiday events</span><br /><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> index <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> index <span class="token operator"><</span> engHols<span class="token punctuation">.</span>length<span class="token punctuation">;</span> index<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">const</span> hol <span class="token operator">=</span> engHols<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Get the current holiday</span><br /> <span class="token keyword">const</span> date <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>hol<span class="token punctuation">.</span>date<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Convert the holiday date to a Date object</span><br /><br /> <span class="token comment">// Skip holidays that are in the past (before the current date)</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>date<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator"><</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token comment">// Skip to the next holiday</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// Create a new schedule for upcoming holidays</span><br /> <span class="token keyword">const</span> newSchedule <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"command"</span><span class="token operator">:</span> <span class="token string">"add"</span><span class="token punctuation">,</span> <span class="token comment">// Command to add a new schedule</span><br /> <span class="token string-property property">"name"</span><span class="token operator">:</span> hol<span class="token punctuation">.</span>title <span class="token operator">+</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"> (</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>date<span class="token punctuation">.</span><span class="token function">getFullYear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token comment">// Holiday name with year</span><br /> <span class="token string-property property">"topic"</span><span class="token operator">:</span> hol<span class="token punctuation">.</span>title<span class="token punctuation">,</span> <span class="token comment">// Holiday title as topic</span><br /> <span class="token string-property property">"expression"</span><span class="token operator">:</span> hol<span class="token punctuation">.</span>date<span class="token punctuation">,</span> <span class="token comment">// Holiday date to use as an expression</span><br /> <span class="token string-property property">"expressionType"</span><span class="token operator">:</span> <span class="token string">"dates"</span><span class="token punctuation">,</span> <span class="token comment">// Define the type as dates</span><br /> <span class="token string-property property">"payload"</span><span class="token operator">:</span> hol<span class="token punctuation">,</span> <span class="token comment">// Send the holiday details as the payload</span><br /> <span class="token string-property property">"payloadType"</span><span class="token operator">:</span> <span class="token string">"json"</span> <span class="token comment">// Specify that the payload is in JSON format</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Add the new schedule to the list of new schedules</span><br /> newSchedules<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>newSchedule<span class="token punctuation">)</span><span class="token punctuation">;</span> <br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Set the topic as empty (not used for now)</span><br />msg<span class="token punctuation">.</span>topic <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span><br /><span class="token comment">// Set the payload to the new schedules array created above</span><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> newSchedules<span class="token punctuation">;</span><br /><br /><span class="token comment">// Return the updated message with new schedules</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-927" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Drag a cron-plus node onto the canvas. Connect the Inject node to the HTTP request node, the HTTP request node to the Function node, and the Function node to the cron-plus node.</li>
<li>Deploy the flow.</li>
</ol>
<p>Now you can check the dynamic schedules list (see how at the end of this section).</p>
<div id="nr-flow-174" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow174 = "\n[{\"id\":\"3608db0d1bd59aa5\",\"type\":\"inject\",\"z\":\"1791a7bd576b3a15\",\"name\":\"Update bank holidays\",\"props\":[{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"00 02 * * 1\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":160,\"y\":60,\"wires\":[[\"252cb7c88de78e45\"]]},{\"id\":\"252cb7c88de78e45\",\"type\":\"http request\",\"z\":\"1791a7bd576b3a15\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"obj\",\"paytoqs\":\"ignore\",\"url\":\"https://www.gov.uk/bank-holidays.json\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":[],\"x\":370,\"y\":60,\"wires\":[[\"cf71e6ad0a983d80\"]]},{\"id\":\"cf71e6ad0a983d80\",\"type\":\"function\",\"z\":\"1791a7bd576b3a15\",\"name\":\"england bank holiday schedules\",\"func\":\"const engHols = msg.payload[\\\"england-and-wales\\\"].events\\n\\n// clear out existing schedules\\nnode.send({topic:'remove-all'})\\n\\nconst newSchedules = []\\nfor(let index = 0; index < engHols.length; index++) {\\n const hol = engHols[index];\\n const date = new Date(hol.date)\\n if (date.valueOf() < Date.now()) {\\n continue\\n }\\n const newSchedule = {\\n \\\"command\\\": \\\"add\\\",\\n \\\"name\\\": hol.title + ` (${date.getFullYear()})`,\\n \\\"topic\\\": hol.title,\\n \\\"expression\\\": hol.date,\\n \\\"expressionType\\\": \\\"dates\\\",\\n \\\"payload\\\": hol,\\n \\\"payloadType\\\": \\\"json\\\"\\n }\\n newSchedules.push(newSchedule) \\n}\\n\\nmsg.topic = ''\\nmsg.payload = newSchedules\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":430,\"y\":120,\"wires\":[[\"abea360f336bbf5c\"]]},{\"id\":\"abea360f336bbf5c\",\"type\":\"cronplus\",\"z\":\"1791a7bd576b3a15\",\"name\":\"\",\"outputField\":\"payload\",\"timeZone\":\"\",\"storeName\":\"\",\"commandResponseMsgOutput\":\"output2\",\"defaultLocation\":\"\",\"defaultLocationType\":\"default\",\"outputs\":2,\"options\":[],\"x\":360,\"y\":200,\"wires\":[[\"dfff5dae3de9e7de\"],[\"7834e47a1631346f\"]]},{\"id\":\"b5a53df2dedde1d2\",\"type\":\"inject\",\"z\":\"1791a7bd576b3a15\",\"name\":\"\",\"props\":[{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"list-all\",\"x\":110,\"y\":180,\"wires\":[[\"abea360f336bbf5c\"]]},{\"id\":\"7834e47a1631346f\",\"type\":\"debug\",\"z\":\"1791a7bd576b3a15\",\"name\":\"list\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":750,\"y\":220,\"wires\":[]},{\"id\":\"dfff5dae3de9e7de\",\"type\":\"debug\",\"z\":\"1791a7bd576b3a15\",\"name\":\"action\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":750,\"y\":180,\"wires\":[]},{\"id\":\"041f7306088daf24\",\"type\":\"inject\",\"z\":\"1791a7bd576b3a15\",\"name\":\"\",\"props\":[{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"remove-all\",\"x\":120,\"y\":220,\"wires\":[[\"abea360f336bbf5c\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow174.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-174') })</script>
<h4 id="dynamic-demo-2" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#dynamic-demo-2"></a> Dynamic Demo 2</h4>
<p>Additionally, I'd like to share another demo that Steve has prepared for the community, which demonstrates dynamic scheduling based on the best energy prices:</p>
<div id="nr-flow-175" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow175 = "\n[{\"id\":\"0f969cd96da84b2e\",\"type\":\"http request\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"obj\",\"paytoqs\":\"ignore\",\"url\":\"https://api.awattar.at/v1/marketdata\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":[],\"x\":450,\"y\":220,\"wires\":[[\"32e08539d8339f74\"]]},{\"id\":\"2a7465d67deae278\",\"type\":\"inject\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"get liveData\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":290,\"y\":220,\"wires\":[[\"0f969cd96da84b2e\"]]},{\"id\":\"32e08539d8339f74\",\"type\":\"function\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"Billigsten 5 Stunden -> msg.liveData\",\"func\":\"var timestamp = Date.now();\\nvar maxLoadingDuration = 5;\\n\\nvar cheapestHours = msg.payload.data\\n .sort((a,b) => a.marketprice - b.marketprice)\\n .slice(0,maxLoadingDuration)\\n .sort((a,b) => a.start_timestamp - b.start_timestamp);\\n var currentHour = cheapestHours.filter(d => d.start_timestamp < timestamp\\n && d.end_timestamp > timestamp);\\nmsg.liveData = {\\n soc:msg.payload,\\n cheapestHours: cheapestHours\\n}\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":\"\",\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":370,\"y\":280,\"wires\":[[\"5c56b3ca5ac4f7c2\"]]},{\"id\":\"89c2acf51e7fbc9c\",\"type\":\"function\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"merge active and new schedules\",\"func\":\"\\nconst inputData = msg.liveData.cheapestHours\\nconst activeSchedules = msg.activeSchedules || []\\n\\n// Clear existing schedules\\nnode.send({ topic: \\\"remove-all-dynamic\\\" });\\n\\n// helper function\\nconst makeSchedule = (start, time, suffix) => {\\n const isStart = start == true || start === \\\"start\\\" || start === \\\"on\\\" || start == 1\\n const title = formatTime(time) + (isStart ? \\\"-on\\\" : \\\"-off\\\")\\n const name = suffix ? `${title} (${suffix}) ` : title\\n return {\\n \\\"command\\\": \\\"add\\\",\\n \\\"name\\\": name,\\n \\\"expression\\\": time,\\n \\\"expressionType\\\": \\\"dates\\\",\\n \\\"payloadType\\\": \\\"str\\\",\\n \\\"payload\\\": isStart ? \\\"start\\\" : \\\"stop\\\",\\n }\\n}\\n// helper function\\nconst formatTime = (date) => {\\n const d = new Date(date)\\n const hh = (\\\"\\\" + d.getHours()).padStart(2, \\\"0\\\")\\n const mm = (\\\"\\\" + d.getMinutes()).padStart(2, \\\"0\\\")\\n return `${hh}:${mm}`\\n}\\n\\n// vars\\nconst newSchedules = []\\nconst mergedSchedules = []\\nconst keepSchedules = []\\n\\nfor (let index = 0; index < inputData.length; index++) {\\n const element = inputData[index];\\n let suffix = ''\\n suffix = element.marketprice + \\\" \\\" + element.unit\\n const startTime = new Date(element.start_timestamp)\\n const endTime = new Date(element.end_timestamp)\\n newSchedules.push(makeSchedule(\\\"start\\\", startTime.valueOf(), suffix))\\n newSchedules.push(makeSchedule(\\\"stop\\\", endTime.valueOf(), suffix))\\n}\\n\\n\\n// if there are any existing schedules not yet operated\\n// and they are before the first in the new data, lets keep them\\nif (newSchedules?.length) {\\n const firstNewSchedule = newSchedules[0]\\n const existingSchedulesBeforeFirstNew = activeSchedules?.filter(e => e.expression < firstNewSchedule.expression)\\n if (existingSchedulesBeforeFirstNew?.length) {\\n keepSchedules.push(...existingSchedulesBeforeFirstNew.map(e => {\\n const n = e.name\\n const k = makeSchedule (e.payload, e.expression) \\n k.name = n.replace(' (keep)', '') + \\\" (keep)\\\"\\n return k\\n }))\\n }\\n} else {\\n // keep all existing active schedules (as there are no new ones)\\n keepSchedules.push(...activeSchedules)\\n}\\n\\nmergedSchedules.push(...newSchedules, ...keepSchedules)\\nmergedSchedules.sort((a, b) => a.expression - b.expression) // order by expression asc\\n\\n// deduplicate the schedules by seeing if there are consecutive on/off schedules\\nlet deduplicatedSchedules = [];\\nlet prevSchedule\\nfor (let i = 0; i < mergedSchedules.length; i++) {\\n const currentSchedule = mergedSchedules[i]\\n if (i === 0) {\\n deduplicatedSchedules.push(currentSchedule);\\n } else {\\n if (currentSchedule.payload === prevSchedule.payload) {\\n // to scheules are the same (start/start or stop/stop)\\n if (currentSchedule.payload === \\\"start\\\") {\\n // ignore this one as the previous one was a \\\"start\\\" (and due to sorting, this one is superfluous)\\n } else if (currentSchedule.payload === \\\"stop\\\") {\\n // since the previous one was a \\\"stop\\\", we need to update the previous schedule to the new stop time\\n prevSchedule.expression = currentSchedule.expression\\n }\\n } else {\\n deduplicatedSchedules.push(currentSchedule); // push the current schedule\\n }\\n }\\n prevSchedule = currentSchedule\\n}\\n\\n// node.warn({newSchedules, keepSchedules, mergedSchedules, deduplicatedSchedules}) // debug\\nmsg.payload = deduplicatedSchedules\\nmsg.topic = \\\"update schedules\\\"\\n\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":740,\"y\":340,\"wires\":[[\"b6a5d42c7751fae2\"]]},{\"id\":\"827e16c3f02d1962\",\"type\":\"inject\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"Clear all scedules\",\"props\":[{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"remove-all-dynamic\",\"x\":310,\"y\":380,\"wires\":[[\"b6a5d42c7751fae2\"]]},{\"id\":\"32a88825fe3edf37\",\"type\":\"switch\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"start\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"start\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":1250,\"y\":280,\"wires\":[[\"e84ba501a1405907\"]]},{\"id\":\"5e5497cff754ef73\",\"type\":\"switch\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"stop\",\"property\":\"topic\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"payload\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":1250,\"y\":320,\"wires\":[[\"b2e8c3606bd04897\"]]},{\"id\":\"e84ba501a1405907\",\"type\":\"debug\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"start something\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":true,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"payload\",\"statusType\":\"auto\",\"x\":1460,\"y\":280,\"wires\":[]},{\"id\":\"b2e8c3606bd04897\",\"type\":\"debug\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"stop something\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":true,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"payload\",\"statusType\":\"auto\",\"x\":1460,\"y\":320,\"wires\":[]},{\"id\":\"b6a5d42c7751fae2\",\"type\":\"cronplus\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"\",\"outputField\":\"payload\",\"timeZone\":\"\",\"storeName\":\"\",\"commandResponseMsgOutput\":\"output2\",\"defaultLocation\":\"\",\"defaultLocationType\":\"default\",\"outputs\":2,\"options\":[],\"x\":1020,\"y\":380,\"wires\":[[\"32a88825fe3edf37\",\"5e5497cff754ef73\"],[\"fe51aaba903612eb\"]]},{\"id\":\"5c56b3ca5ac4f7c2\",\"type\":\"link call\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"\",\"links\":[\"ee57d78d1c769d1b\"],\"linkType\":\"static\",\"timeout\":\"30\",\"x\":730,\"y\":280,\"wires\":[[\"89c2acf51e7fbc9c\"]]},{\"id\":\"b4cf44f6c7bd1461\",\"type\":\"link in\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"link in 4\",\"links\":[\"29dcf0872d3f7ca4\"],\"x\":505,\"y\":320,\"wires\":[[\"5c56b3ca5ac4f7c2\"]]},{\"id\":\"fe51aaba903612eb\",\"type\":\"link out\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"link out 17\",\"mode\":\"link\",\"links\":[\"a1c12404f726b02f\"],\"x\":1215,\"y\":380,\"wires\":[]},{\"id\":\"51b3de02bcf463a3\",\"type\":\"group\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"test data\",\"style\":{\"label\":true},\"nodes\":[\"6b39e36054edcc22\",\"32388c4b0af7a5b1\",\"73a529bfd3742503\",\"43aa8690b4167bcc\",\"dae8031ee5f37eb5\",\"29dcf0872d3f7ca4\"],\"x\":214,\"y\":579,\"w\":522,\"h\":122},{\"id\":\"6b39e36054edcc22\",\"type\":\"inject\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":320,\"y\":620,\"wires\":[[\"32388c4b0af7a5b1\"]]},{\"id\":\"32388c4b0af7a5b1\",\"type\":\"template\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"name\":\"cheapestHours sample\",\"field\":\"liveData\",\"fieldType\":\"msg\",\"format\":\"json\",\"syntax\":\"mustache\",\"template\":\"{\\n \\\"soc\\\": {},\\n \\\"cheapestHours\\\": [\\n {\\n \\\"start_timestamp\\\": \\\"2025-05-07 07:00:00\\\",\\n \\\"end_timestamp\\\": \\\"2025-05-07 12:00:00\\\",\\n \\\"marketprice\\\": 66.02,\\n \\\"unit\\\": \\\"Eur/MWh\\\"\\n },\\n {\\n \\\"start_timestamp\\\": \\\"2025-05-07 20:00:00\\\",\\n \\\"end_timestamp\\\": \\\"2025-05-07 21:00:00\\\",\\n \\\"marketprice\\\": 62.48,\\n \\\"unit\\\": \\\"Eur/MWh\\\"\\n }\\n ]\\n }\",\"output\":\"json\",\"x\":500,\"y\":620,\"wires\":[[\"dae8031ee5f37eb5\"]]},{\"id\":\"73a529bfd3742503\",\"type\":\"inject\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":320,\"y\":660,\"wires\":[[\"43aa8690b4167bcc\"]]},{\"id\":\"43aa8690b4167bcc\",\"type\":\"template\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"name\":\"cheapestHours overlap\",\"field\":\"liveData\",\"fieldType\":\"msg\",\"format\":\"json\",\"syntax\":\"mustache\",\"template\":\"{\\n \\\"soc\\\": {},\\n \\\"cheapestHours\\\": [\\n {\\n \\\"start_timestamp\\\": \\\"2025-05-07 08:00:00\\\",\\n \\\"end_timestamp\\\": \\\"2025-05-07 11:00:00\\\",\\n \\\"marketprice\\\": 66.02,\\n \\\"unit\\\": \\\"Eur/MWh\\\"\\n },\\n {\\n \\\"start_timestamp\\\": \\\"2025-05-07 20:00:00\\\",\\n \\\"end_timestamp\\\": \\\"2025-05-07 21:00:00\\\",\\n \\\"marketprice\\\": 62.48,\\n \\\"unit\\\": \\\"Eur/MWh\\\"\\n }\\n ]\\n }\",\"output\":\"json\",\"x\":510,\"y\":660,\"wires\":[[\"dae8031ee5f37eb5\"]]},{\"id\":\"dae8031ee5f37eb5\",\"type\":\"junction\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"x\":660,\"y\":640,\"wires\":[[\"29dcf0872d3f7ca4\"]]},{\"id\":\"29dcf0872d3f7ca4\",\"type\":\"link out\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"51b3de02bcf463a3\",\"name\":\"link out 16\",\"mode\":\"link\",\"links\":[\"b4cf44f6c7bd1461\"],\"x\":695,\"y\":640,\"wires\":[]},{\"id\":\"f3ff5331698e1750\",\"type\":\"group\",\"z\":\"f559cf0eb4eca11c\",\"name\":\"Subroutine for getting active schedules\",\"style\":{\"label\":true},\"nodes\":[\"ee57d78d1c769d1b\",\"9956e352b1728160\",\"aba3e0f7de7c6f54\",\"cc753a34602daf70\",\"e4b0cb5dbf1fcbcc\",\"a1c12404f726b02f\"],\"x\":594,\"y\":439,\"w\":712,\"h\":82},{\"id\":\"ee57d78d1c769d1b\",\"type\":\"link in\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"get-active-dynamic schedules\",\"links\":[],\"x\":740,\"y\":480,\"wires\":[[\"9956e352b1728160\"]],\"l\":true},{\"id\":\"9956e352b1728160\",\"type\":\"change\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"export-active-dynamic\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":905,\"y\":480,\"wires\":[[\"b6a5d42c7751fae2\"]],\"l\":false},{\"id\":\"aba3e0f7de7c6f54\",\"type\":\"change\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"\",\"rules\":[{\"t\":\"move\",\"p\":\"payload.result\",\"pt\":\"msg\",\"to\":\"activeSchedules\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1215,\"y\":480,\"wires\":[[\"cc753a34602daf70\"]],\"l\":false},{\"id\":\"cc753a34602daf70\",\"type\":\"link out\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"link-return\",\"mode\":\"return\",\"links\":[],\"x\":1265,\"y\":480,\"wires\":[]},{\"id\":\"e4b0cb5dbf1fcbcc\",\"type\":\"switch\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"\",\"property\":\"_linkSource\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"istype\",\"v\":\"array\",\"vt\":\"array\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":1,\"x\":1165,\"y\":480,\"wires\":[[\"aba3e0f7de7c6f54\"]],\"l\":false},{\"id\":\"a1c12404f726b02f\",\"type\":\"link in\",\"z\":\"f559cf0eb4eca11c\",\"g\":\"f3ff5331698e1750\",\"name\":\"link in 12\",\"links\":[\"fe51aaba903612eb\"],\"x\":1105,\"y\":480,\"wires\":[[\"e4b0cb5dbf1fcbcc\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow175.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-175') })</script>
<p>If you need to see the dynamic schedules of a specific node, double-click on it and click on the 'Dynamic Schedules' button from the configuration panel. This will show all of the dynamic schedules associated with that node.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/dynamic-schedules-list-ZIgHZtZcbr-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Dynamic Schedules' button and the list of all dynamically scheduled events." loading="lazy" decoding="async" src="https://flowfuse.com/img/dynamic-schedules-list-ZIgHZtZcbr-800.webp" width="800" height="946" /></picture>
<em>Image showing the 'Dynamic Schedules' button and the list of all dynamically scheduled events.</em></p>
<p>As you can see, creating flexible and dynamic cron schedules Node-RED can give you complete control over your automation tasks. Whether it's simple, recurring events, or complex, time-sensitive triggers, the combination of cron expressions and dynamic controls allows for smarter, more efficient workflows.</p>
<p>Take some time to explore these features and experiment with your own schedules. The more you play around with them, the better you'll understand how to tailor your flows to meet your exact needs.</p>
<p>Thanks for reading, and happy automating! If you run into any questions or need assistance along the way, don’t hesitate to reach out. We’re here to help!</p>
<p>If you’re using Node-RED in your production environment, it’s important to keep your instances organized, scalable, and secure. FlowFuse can help with that by making it easier to manage and maintain your Node-RED setup, while also supporting faster deployment, scaling, and improved security.</p>
<p><a href="https://flowfuse.com/contact-us/">Contact us</a> now to learn more.</p>
<h2 id="wrapping-up" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/designing-flexible-cron-schedules-in-flowfuse-with-node-red/#wrapping-up"></a> Wrapping Up</h2>
<p>Designing flexible and intelligent schedules is a key part of building robust automation with Node-RED. Whether you’re triggering actions based on time, solar events, or dynamic runtime conditions, the cron-plus node gives you a powerful set of tools to fine-tune when your flows should run.</p>
<p>By combining these scheduling techniques with the management features of FlowFuse, you can confidently build and operate reliable automation systems at any scale.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/How to Embed Webpages on the FlowFuse DashboardLearn how to embed external content like maps, reports, and widgets onto your FlowFuse dashboard.2025-05-13T00:00:00ZSumit Shinde<p>When you build a dashboard, sometimes you need more than just internal data. Maybe it’s a live map, a report hosted elsewhere, or another dashboard — whatever it is, having to switch tabs breaks the flow. FlowFuse lets you embed external content like web pages, dashboards, PDFs, and widgets right into your dashboard. This guide shows you how to do exactly that — step by step — so your team has everything they need, all in one place.</p>
<!--more-->
<h2 id="why-embed-webpages-in-your-dashboard%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#why-embed-webpages-in-your-dashboard%3F"></a> Why Embed Webpages in Your Dashboard?</h2>
<p>Embedding external content directly into your FlowFuse dashboard can make your life a lot easier. Here’s why it’s worth considering:</p>
<ul>
<li>
<p><strong>Streamline Your Workflow:</strong> No more hopping between tabs or switching apps. Everything you need can be in one place.</p>
</li>
<li>
<p><strong>Save Time on Rework:</strong> If you’ve already built a report, chart, or page somewhere else, just embed it into your dashboard. No need to start from scratch.</p>
</li>
<li>
<p><strong>Quick Decisions:</strong> Having all your key data together helps you see what’s important at a glance and act faster.</p>
</li>
<li>
<p><strong>Cut Down on Clicks:</strong> The fewer actions needed to get to your information, the more time you can spend on actually getting things done.</p>
</li>
</ul>
<h2 id="how-to-embed-webpages-on-your-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#how-to-embed-webpages-on-your-flowfuse-dashboard"></a> How to Embed Webpages On your FlowFuse Dashboard</h2>
<p>Embedding external content into your FlowFuse dashboard is straightforward and flexible. In this section, you'll learn two ways to do it: using a direct URL and using embed code. We’ll also cover common issues you might face and how to fix them. Lastly, you'll see how to embed one FlowFuse dashboard inside another, making it easier to centralize important views in one place.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#prerequisites"></a> Prerequisites</h3>
<p>Before you begin embedding webpages on FlowFuse Dashboard, make sure you have the following:</p>
<ul>
<li><strong>Running FlowFuse Instance:</strong> Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out our <a href="https://app.flowfuse.com/account/create">free trial</a>.</li>
<li><strong>FlowFuse Dashboard:</strong> Ensure you have <a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">FlowFuse Dashboard</a> (also known as Node-RED Dashboard 2.0 in the community) installed and properly configured on your instance.</li>
<li><strong>@flowfuse/node-red-dashboard-2-ui-iframe:</strong> Ensure you have <a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard-2-ui-iframe">node-red-dashboard-2-ui-iframe</a> installed.</li>
</ul>
<h4 id="step-1%3A-obtain-the-url-or-embed-code-of-the-webpage-you-want-to-embed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#step-1%3A-obtain-the-url-or-embed-code-of-the-webpage-you-want-to-embed"></a> Step 1: Obtain the URL or Embed Code of the Webpage You Want to Embed</h4>
<p>The first step is identifying and grabbing the URL of the webpage or external content you want to embed into your FlowFuse dashboard. This could be a live dashboard, report, Google Maps, video, or any other type of content.</p>
<p>When embedding content from third-party sources, make sure the following conditions the page or content you're embedding should be publicly accessible or have the proper permissions for embedding. For example, private reports or webpages require login credentials or an API key, which should be handled securely.</p>
<p>Some websites may restrict embedding through iframes due to <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">Cross-Origin Resource Sharing (CORS)</a> policies. Check whether the external site allows embedding if the content doesn’t load properly.</p>
<p>Many platforms, such as YouTube and Google Maps, provide a specific "embed code or URL" that is more suitable for embedding. Ensure you use the correct embed link or code these platforms offer to guarantee smooth integration.</p>
<p>Once the URL or embed code is ready, move on to the next step.</p>
<h4 id="step-2%3A-embedding-the-webpage-on-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#step-2%3A-embedding-the-webpage-on-flowfuse-dashboard"></a> Step 2: Embedding the Webpage on FlowFuse Dashboard</h4>
<p>Now that you’ve gathered the URL or embed code for the external content you want to embed, it’s time to add it to your FlowFuse dashboard. Below are two methods for embedding external content:</p>
<h5 id="2.1-embed-via-url" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#2.1-embed-via-url"></a> 2.1 Embed via URL</h5>
<p>The easiest and most straightforward way to embed external content is by using the URL. FlowFuse's iframe node allows you to use an external URL directly. Here’s how to do it:</p>
<ol>
<li>Drag the ui_iframe widget onto the canvas.</li>
<li>Double-click the widget to open its configuration dialog.
<ul>
<li>Create a new group for it to render in.</li>
<li>Set the size (width and height).</li>
<li>Enter the URL you want to embed.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/if_frame-config-YNN2fdrz57-754.avif 754w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/if_frame-config-YNN2fdrz57-754.webp 754w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="ui_iframe widget configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/if_frame-config-YNN2fdrz57-754.jpeg" width="754" height="618" /></picture>
<em>iframe widget configuration</em></p>
<ol start="3">
<li>Click Done and then Deploy.</li>
</ol>
<p>For a quick hands-on practice, you can try embedding a weather widget, Google Maps, Google Calendar, or a hosted PDF on your dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/weather-widget-3rikUepPuA-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/weather-widget-3rikUepPuA-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Weather widget embedded in FlowFuse Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/weather-widget-3rikUepPuA-650.jpeg" width="650" height="959" /></picture>
<em>Weather widget embedded in FlowFuse Dashboard</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/google-map-AmqLkyDhCg-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/google-map-AmqLkyDhCg-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Google Map embedded in FlowFuse Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/google-map-AmqLkyDhCg-650.jpeg" width="650" height="580" /></picture>
<em>Google Map embedded in FlowFuse Dashboard</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pdf-LBX0G14bqI-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/pdf-LBX0G14bqI-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="PDF embedded in FlowFuse Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/pdf-LBX0G14bqI-650.jpeg" width="650" height="718" /></picture>
<em>PDF embedded in FlowFuse Dashboard</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/google-calendar-LFAh8qnuja-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/google-calendar-LFAh8qnuja-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Google Calendar embedded in FlowFuse Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/google-calendar-LFAh8qnuja-650.jpeg" width="650" height="717" /></picture>
<em>Google Calendar embedded in FlowFuse Dashboard</em></p>
<h5 id="2.2-embed-via-html-embed-code" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#2.2-embed-via-html-embed-code"></a> 2.2 Embed via HTML Embed Code</h5>
<p>If you have an embed code from third-party services, you can use this code to embed the content into your FlowFuse dashboard. Here’s how to do it:</p>
<ol>
<li>Drag the <strong>ui-template</strong> widget onto the canvas.</li>
<li>Double-click on it to open its configuration dialog.
<ul>
<li>Create a new group for it to render in.</li>
<li>Set the size (width and height).</li>
</ul>
</li>
<li>Paste your embed code into the template widget. Most embed codes include both HTML and JavaScript, so follow this structure:</li>
</ol>
<div style="position: relative" id="code-container-180">
<pre class="language-html"><code id="code-180" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Paste your HTML code here --></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token comment">// Paste your JavaScript code here</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-180" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Some services provide an <strong>iframe</strong> tag. In this case, you need only copy the URL and use the embedding via the URL method.</p>
<ol start="4">
<li>Click <strong>Done</strong> and <strong>Deploy</strong> to save your changes once you've added the code.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/weather-widget-zde1sWc3Uo-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Animated weather widget on the Flowfuse dashboard, embedded with code." loading="lazy" decoding="async" src="https://flowfuse.com/img/weather-widget-zde1sWc3Uo-800.webp" width="800" height="1007" /></picture>
<em>Animated weather widget on the Flowfuse dashboard, embedded with code.</em></p>
<p>Now that you’ve learned how to embed external content into your FlowFuse dashboard, one day you may need to embed your FlowFuse dashboard elsewhere—either on another FlowFuse dashboard or an external site. If you’ve tried and found that it’s not working as expected.</p>
<p>This is a security feature designed to protect your data. FlowFuse dashboards, like many other web applications, implement security policies such as Cross-Origin Resource Sharing (CORS) and the X-Frame-Options header. These policies ensure that your dashboard is only viewed in trusted environments, preventing malicious sites from tampering with your data or exposing it to unauthorized users.</p>
<p>However, if you need to embed your FlowFuse dashboard into other websites or on another FlowFuse dashboard, it’s possible to do so with some configuration changes. Below is a section on how to enable embedding securely of FlowFuse Dashboard:</p>
<h3 id="enabling-embedding-of-flowfuse-dashboards" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#enabling-embedding-of-flowfuse-dashboards"></a> Enabling Embedding of FlowFuse Dashboards</h3>
<ol>
<li>Go to your FlowFuse instance settings.</li>
<li>Switch to the <strong>editor</strong> settings and enable <strong>"Allow Dashboard to be embedded in an iFrame"</strong></li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/allow-dashboard-embedding-CA17k9xWjl-930.avif 930w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/allow-dashboard-embedding-CA17k9xWjl-930.webp 930w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse instance settings showing the option to allow dashboard embedding in an iframe." loading="lazy" decoding="async" src="https://flowfuse.com/img/allow-dashboard-embedding-CA17k9xWjl-930.jpeg" width="930" height="892" /></picture>
<em>FlowFuse instance settings showing the option to allow dashboard embedding in an iframe.</em></p>
<ol start="3">
<li>Click <strong>Save Settings and restart</strong> your instance for the changes to take effect.</li>
</ol>
<p><a href="https://flowfuse.com/blueprints/manufacturing/oee-dashboard/"><picture><source type="image/avif" srcset="https://flowfuse.com/img/embedding-flowfuse-oee-dashboard-6_UDX7eSeN-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/embedding-flowfuse-oee-dashboard-6_UDX7eSeN-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse OEE Dashboard embedded in another FlowFuse dashboard." loading="lazy" decoding="async" src="https://flowfuse.com/img/embedding-flowfuse-oee-dashboard-6_UDX7eSeN-1920.jpeg" width="1920" height="1097" /></picture></a>
<em>FlowFuse OEE Dashboard embedded in another FlowFuse dashboard.</em></p>
<p>FlowFuse dashboards offer great flexibility, making it easy to embed external content like weather widgets, maps, and PDFs directly into your workspace. With a few simple steps, you can also embed FlowFuse dashboards into other websites, ensuring everything you need is in one place.</p>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#up-next"></a> Up Next</h2>
<p>If you're interested in learning more about embedding webpages or enhancing your FlowFuse dashboards, check out the following blog articles:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/">Mapping Location on Dashboard</a>: In this article, we dive into how you can embed location maps into your FlowFuse dashboard. Learn how to map locations, track real-time data, and make your dashboards more interactive and informative.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/displaying-embeded-webpages-on-node-red-dashboard/#">Generating PDF Reports with Node-RED and FlowFuse</a>: This guide explains how to generate PDF reports directly with Node-RED and FlowFuse and how to preview these reports within your FlowFuse dashboards.</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/FlowFuse 2.17: Easier remote instance onboarding, Dashboard blueprint, PDF generation, and moreBuild a dashboard in one click, create PDF reports from your data, and import instances when installing Device Agent2025-05-08T00:00:00ZGreg Stoutenburg<p>This release is focused on improvements that help you get up and running with FlowFuse much more quickly. Our vision is that you can get started with FlowFuse and have a successful application up and running, from scratch, in hours, not days, weeks or months. This release gets us closer to that vision by focusing on improvements to the installation process for the Device Agent, and two new Blueprints.</p>
<!--more-->
<h2 id="device-agent-onboarding" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#device-agent-onboarding"></a> Device Agent onboarding</h2>
<p>If you already had a Node-RED instance running on a device, it was a bit of a hassle to get it connected to FlowFuse as a remote instance. You had to install the Device Agent, find that it clashed with your existing instance, turn off that instance, run the Device Agent again, find that you can't import your existing instance...and so on, until finally getting copies of your flows and adding them to FlowFuse manually.</p>
<p>Lots of friction. We heard you.</p>
<p>Now, when you install the Device Agent, it will automatically scan for an existing Node-RED instance running locally and help you import its flows into FlowFuse.</p>
<p>This streamlines the whole process, saving you valuable time and effort. It ensures you can manage the Node-RED flows running on your edge device as quickly as possible.</p>
<h2 id="blueprint%3A-getting-started-with-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#blueprint%3A-getting-started-with-dashboard"></a> Blueprint: Getting Started with Dashboard</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-HSLPzMV-0L-650.avif 650w, https://flowfuse.com/img/dashboard-HSLPzMV-0L-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-HSLPzMV-0L-650.webp 650w, https://flowfuse.com/img/dashboard-HSLPzMV-0L-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/dashboard-HSLPzMV-0L-650.jpeg 650w, https://flowfuse.com/img/dashboard-HSLPzMV-0L-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Getting Started with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-HSLPzMV-0L-650.jpeg" width="1300" height="719" /></picture>
<em>Screenshot of Getting Started with Dashboard</em></p>
<p>One of the most popular features of Node-RED is Dashboard 2.0. And starting now, getting started with Dashboard is as simple as one click.</p>
<p>Our new Getting Started with Dashboard Blueprint deploys to a new Node-RED instance in a single click and demonstrates several examples of how to use Dashboard. These include various ways of visualizing data, the data entry widget that allows direct entry to a table, and audit log creation.</p>
<p>New Dashboard nodes are added on a regular basis, and we will keep in mind which ones users would like help using as we iterate on this Blueprint.</p>
<p>To put this Blueprint to use, check out the Blueprint page for <a href="https://flowfuse.com/blueprints/getting-started/dashboard/">Getting Started with Dashboard.</a></p>
<h2 id="blueprint%3A-pdf-report-generator" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#blueprint%3A-pdf-report-generator"></a> Blueprint: PDF Report Generator</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pdf-flow-Y-9Gsd20bx-569.avif 569w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/pdf-flow-Y-9Gsd20bx-569.webp 569w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of PDF Report Generator" loading="lazy" decoding="async" src="https://flowfuse.com/img/pdf-flow-Y-9Gsd20bx-569.jpeg" width="569" height="383" /></picture>
<em>Screenshot of PDF Report Generator</em></p>
<p>Until now, creating a presentable report using data accessed in your Node-RED instance was a manual effort.</p>
<p>This new Blueprint makes it very easy to create PDF reports from your data. Using SQLite nodes and connecting to your database, this Blueprint enables drag-and-drop field selection.</p>
<p>To get you started using this Blueprint, we've included sample data that is generated automatically.</p>
<p>Instructions for using this Blueprint, including how to map fields from your database to our preconfigured fields, <a href="https://flowfuse.com/blueprints/manufacturing/pdf-report-generator/">check out the Blueprint page.</a></p>
<h2 id="where-are-we-headed%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#where-are-we-headed%3F"></a> Where are we headed?</h2>
<p>We are focused on making it as easy as possible to get started with Node-RED, to manage Node-RED instances, and start building applications quickly. Some improvements on the way include <a href="https://github.com/FlowFuse/flowfuse/issues/5415">pulling from a Git repo</a> (we shipped Git push last release), including <a href="https://github.com/FlowFuse/flowfuse/issues/5179">Blueprints for self-hosted installations</a>, and <a href="https://github.com/FlowFuse/flowfuse/issues/223">better information about resource usage</a>. We're also busy at work on a number of core Node-RED items for the forthcoming 4.1.0 release.</p>
<p>With this work and more on the way, we are continuing to deliver our vision of FlowFuse being the best way to unlock your industrial data, integrate everything and optimize faster.</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#what-else-is-new%3F"></a> What else is new?</h2>
<p>For a full list of everything that went into our 2.17 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/flowfuse-release-2-17/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/Part 1: Building an Andon Task Manager with FlowFuseBuild a real-time Andon Task Manager with FlowFuse and Node-RED, covering key features, dashboard design, and data storage.2025-05-08T00:00:00ZSumit Shinde<p>In modern manufacturing and service environments, speed and transparency are critical for addressing issues as they arise. An Andon system helps achieve this by enabling frontline workers to signal problems in real time, triggering quick responses from support teams.</p>
<!--more-->
<p>However, many manufacturers struggle to find a solution that truly fits their needs. Some tools lack essential features, while others are overloaded with unnecessary ones that add complexity.</p>
<p>This blog series introduces a practical approach to building a real-time Andon Task Manager using FlowFuse and Node-RED. In this first part, the focus is on understanding the concept of an Andon system and laying the foundation for the solution.</p>
<h2 id="what-is-the-andon-task-manager%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#what-is-the-andon-task-manager%3F"></a> What is the Andon Task Manager?</h2>
<p>The Andon Task Manager is a digital system designed to streamline real-time issue reporting, escalation, and resolution tracking. Inspired by the traditional Andon systems used in lean manufacturing, it brings these concepts into a modern, cloud-enabled environment.</p>
<p>At its core, it’s a communication and response tool designed to improve transparency and speed on the factory floor or within service teams. Frontline workers can quickly raise issues—like equipment breakdowns, material shortages, or support needs—which are immediately sent to the right person or team. Once the issue is resolved, the responder updates the status so everyone stays informed and the task is properly closed.</p>
<h2 id="what-problem-it-solves%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#what-problem-it-solves%3F"></a> What Problem It Solves?</h2>
<p>In a typical manufacturing environment, multiple processes run simultaneously across large factory floors. Each area—or line—has specific machinery, workflows, and potential points of failure. When something goes wrong, quick and clear communication is essential. However, factories are often spread out, and support teams are divided across different departments (e.g., maintenance, quality control, safety, etc.).</p>
<p>In many cases, workers rely on informal or manual systems—such as radio calls, phone messages, or shouting across the floor—to report issues. These methods are inefficient, error-prone, and often delay response times. The lack of a structured, real-time communication system leads to:</p>
<ul>
<li>Delayed responses because support staff are unaware of new issues</li>
<li>Lack of visibility into the status of reported issues
= No accountability for weather the issue is acknowledged/resolved or not</li>
<li>Unstructured logging that makes follow-up or audits difficult</li>
</ul>
<p>The Andon Task Manager solves this by acting as a centralized system where any frontline worker can quickly raise an issue. Once submitted, the request is instantly visible to the relevant department—without needing someone to manually assign it. This enables self-routing and real-time visibility, ensuring the right people take action quickly and efficiently, even when the requester and responder are in completely different parts of the factory.</p>
<h2 id="planning-the-andon-task-manager" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#planning-the-andon-task-manager"></a> Planning the Andon Task Manager</h2>
<p>At the core of the system is the concept of a request. Every request represents a task or issue raised by an operator. To ensure traceability and clarity, each request should include key details. This structured format makes it easier for departments to manage and resolve issues efficiently.</p>
<p>Each request must include the following:</p>
<ul>
<li><code>id</code>: A unique identifier for the request.</li>
<li><code>line</code>: The line or machine where the issue was raised.</li>
<li><code>department</code>: The department responsible for resolving the issue.</li>
<li><code>created</code>: The timestamp when the request was created.</li>
<li><code>acknowledged</code>: Timestamp indicating when the request was acknowledged.</li>
<li><code>resolved</code>: Timestamp indicating when the issue was resolved.</li>
<li><code>note</code>: Text added by users for context or follow-up.
Only predefined values for line and department should be allowed. These values will be managed through admin settings to ensure consistency across the system.</li>
</ul>
<h3 id="defining-key-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#defining-key-features"></a> Defining Key Features</h3>
<p>The system needs to support core operations that reflect how issues are reported and resolved in real-life factory environments. These features help ensure that tasks are handled efficiently and that everyone involved knows the current status.</p>
<p>The essential features include:</p>
<ul>
<li>Request creation: Users select the line and department, enter a note, and submit a request.</li>
<li>Acknowledge requests: A responder can mark a request as acknowledged once they start working on it.</li>
<li>Resolve requests: After resolving the issue, the responder marks it as resolved.</li>
<li>View filtering: Requests can be filtered by line or department.</li>
<li>Admin tools: Admins can add and manage the list of departments and lines.</li>
<li>Status display: Requests display their current state — pending, acknowledged, or resolved.</li>
<li>Alerts: Visual or sound alerts for unacknowledged requests after a time threshold.</li>
</ul>
<p>Each of these actions will be timestamped to provide a clear history of who did what and when.</p>
<h3 id="dashboard-visualization-%26-ui-design" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#dashboard-visualization-%26-ui-design"></a> Dashboard Visualization & UI Design</h3>
<p>The next step after defining the core features is to design an intuitive and efficient dashboard for both frontline workers and admin users. A well-organized interface ensures quick interactions and smooth navigation, especially in settings where timely responses are crucial.</p>
<p>The system will support two user roles: admins and regular users. Regular users will have access to features such as submitting requests, viewing requests by department or line, and managing tasks within their area (e.g., acknowledging and resolving requests). Admins will have additional capabilities, including creating and managing departments and lines, and accessing all request data.</p>
<p>The regular user's view will be a single-page interface with dynamic content updates. Rather than traditional page navigation, content will update based on the user’s selection of a department or line. For example, when a user selects a production line, the request list and relevant controls will automatically update to display only the requests related to that line.</p>
<p>The admin view will have a dedicated view, including a form for creating new lines or departments and a table displaying all requests. This view will also feature a menu for quickly switching between specific department or line section, improving system management efficiency.</p>
<p>This design keeps the interface focused and responsive. It avoids unnecessary complexity while providing all necessary tools for users to perform their tasks efficiently — whether they are reporting an issue or managing overall operations.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-admin-veiw-m3w2JM-jkK-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-admin-veiw-m3w2JM-jkK-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager." loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-admin-veiw-m3w2JM-jkK-1920.jpeg" width="1920" height="1009" /></picture>
<em>The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/line-menu-3sHT4lKMOl-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/line-menu-3sHT4lKMOl-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager." loading="lazy" decoding="async" src="https://flowfuse.com/img/line-menu-3sHT4lKMOl-1920.jpeg" width="1920" height="1008" /></picture>
<em>The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/line-page-uB1TsCQD3L-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/line-page-uB1TsCQD3L-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager." loading="lazy" decoding="async" src="https://flowfuse.com/img/line-page-uB1TsCQD3L-1920.jpeg" width="1920" height="1012" /></picture>
<em>The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/department-menu-u2aVyHXnht-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/department-menu-u2aVyHXnht-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager." loading="lazy" decoding="async" src="https://flowfuse.com/img/department-menu-u2aVyHXnht-1920.jpeg" width="1920" height="1006" /></picture>
<em>The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/department-wise-cqPV05mx7q-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/department-wise-cqPV05mx7q-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager." loading="lazy" decoding="async" src="https://flowfuse.com/img/department-wise-cqPV05mx7q-1920.jpeg" width="1920" height="1008" /></picture>
<em>The following dashboard image illustrates the intended design and key objectives of our Andon Task Manager.</em></p>
<h3 id="storage-mechanism" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#storage-mechanism"></a> Storage Mechanism</h3>
<p>To ensure a simple and efficient data management system for the Andon Task Manager, we will use SQLite to store user requests. SQLite is a lightweight, easy-to-manage database that is well-supported in Node-RED through the <code>node-red-contrib-sqlite node</code>. This makes it an ideal choice for local deployments or scenarios where a lightweight database is needed.</p>
<p>For dynamic runtime data—such as the user's selected line or department, as well as the full list of available lines and departments—FlowFuse’s built-in <a href="https://flowfuse.com/docs/user/persistent-context/">context storage</a> will be utilized. This solution allows for fast access to real-time data while maintaining persistent state across sessions, without introducing unnecessary database complexity or overhead.</p>
<p>By using both SQLite for structured request data and context storage for dynamic, session-based information, the system remains efficient and easy to maintain.</p>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/building-andon-task-manager-with-ff/#up-next"></a> Up Next</h2>
<p>In the next part of this series, we will focus on developing the Lines view for regular users, along with the navigation menu for switching between different line sections. Later, we will cover the development of the lines view and Admin interface.</p>
<p>But if you can't wait to get started right away, don’t worry! You can <a href="https://app.flowfuse.com/account/create">register</a> for FlowFuse and get started with our ready-made <a href="https://flowfuse.com/blueprints/manufacturing/andon-task/">Andon Task Manager blueprint</a>, which is pre-configured for easy deployment. Stay tuned for the next installment to continue your journey toward building a comprehensive, real-time Andon Task Manager solution.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/How to Generate PDF Reports Using Node-RED in FlowFuseLearn how to automate the generation of dynamic PDF reports within Node-RED and FlowFuse.2025-05-07T00:00:00ZSumit Shinde<p>Generating PDF reports is a common need in many workflows—whether you're logging data, sharing results, or creating summaries. With Node-RED and FlowFuse, you can easily automate turning your data into well-structured PDF files. This guide will show you how to set up step-by-step PDF report generation using simple tools and flows.</p>
<!--more-->
<p>Generating reports allows you to capture snapshots of critical data, summarize system activities, and distribute insights in an easy-to-read and stored format. PDF is one of the most universally accepted formats for sharing documents, making it ideal for delivering structured information from your Node-RED flows.</p>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#prerequisites"></a> Prerequisites</h2>
<p>Before you begin, make sure the following requirements are met:</p>
<ul>
<li>You have an active <a href="https://app.flowfuse.com/">FlowFuse account</a> and a running FlowFuse instance.</li>
<li>You are familiar with creating and deploying basic flows in Node-RED. If not, consider taking the <a href="https://node-red-academy.learnworlds.com/course/node-red-getting-started">Node-RED Fundamentals Course</a> sponsored by FlowFuse.</li>
<li>Ensure you have installed <code>flowfuse/node-red-dashboard</code> <code>@flowfuse/node-red-dashboard-2-ui-iframe</code> and <code>node-red-contrib-sqlite</code> (The SQLite node is required for the demo data generation flow we provided. If you're not using that flow, you can skip this.).</li>
</ul>
<h2 id="setting-up-pdf-generation-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#setting-up-pdf-generation-in-node-red"></a> Setting Up PDF Generation in Node-RED</h2>
<p>Once the prerequisites are in place, the next step is setting up your Node-RED environment to generate PDF reports. In this section, we will go over how to install the necessary Node-RED node and configure a flow to generate PDF reports.</p>
<h3 id="step-1%3A-install-the-%40platmac%2Fnode-red-pdfbuilder" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#step-1%3A-install-the-%40platmac%2Fnode-red-pdfbuilder"></a> Step 1: Install the @platmac/node-red-pdfbuilder</h3>
<p>The <a href="https://flows.nodered.org/node/@platmac/node-red-pdfbuilder">platmac/node-red-pdfbuilder</a> node is the primary node for creating PDF reports in Node-RED. To install this node:</p>
<ol>
<li>Open your Node-RED editor.</li>
<li>Navigate to the "Manage palette" section from the top-right menu.</li>
<li>Click on the "Install" tab and search for <code>@platmac/node-red-pdfbuilder</code>.</li>
<li>Click "Install" to add the node to your palette.</li>
</ol>
<p>This node allows you to dynamically generate PDFs from various inputs, which is exactly what you will need to generate reports.</p>
<p>If you haven't installed the <code>@flowfuse/node-red-dashboard</code>, <code>@flowfuse/node-red-dashboard-2-ui-iframe</code> and <code>node-red-contrib-sqlite</code> nodes, you can install them similarly.</p>
<h3 id="step-2%3A-understanding-how-to-use-the-pdfbuilder-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#step-2%3A-understanding-how-to-use-the-pdfbuilder-node"></a> Step 2: Understanding How to Use the Pdfbuilder Node</h3>
<p>Now that the required node is installed, let's dive into how to use it and how to leverage the different attributes to customize your PDF reports. The pdfbuilder node in Node-RED simplifies generating PDFs by allowing you to define content, layout, and styling directly in your flow.</p>
<p>The key advantage of using pdfbuilder node is that it operates server-side, meaning PDFs can be generated automatically without a browser or manual interaction. This makes it ideal for automated workflows where consistent, programmatically created documents are needed.</p>
<p>When working with this node, you can use various attributes to customize the content and layout of the PDF, such as text, tables, images, page sizes, margins, headers, footers, and more. Below are the most commonly used attributes:</p>
<table>
<thead>
<tr>
<th><strong>Attribute</strong></th>
<th><strong>Description</strong></th>
<th><strong>Example</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>content</code></td>
<td>Defines the content of the PDF (text, tables, images, etc.).</td>
<td><code>{ "content": "Hello, World!" }</code></td>
</tr>
<tr>
<td><code>style</code></td>
<td>Specifies the style for content (font size, font family, etc.).</td>
<td><code>{ "style": "headerStyle" }</code></td>
</tr>
<tr>
<td><code>layout</code></td>
<td>Defines the layout of a table (e.g., 'lightHorizontalLines', 'noBorders').</td>
<td><code>{ "layout": "lightHorizontalLines" }</code></td>
</tr>
<tr>
<td><code>pageSize</code></td>
<td>Defines the page size for the PDF.</td>
<td><code>{ "pageSize": "A4" }</code></td>
</tr>
<tr>
<td><code>pageMargins</code></td>
<td>Sets the margins for the PDF (left, top, right, bottom).</td>
<td><code>{ "pageMargins": [40, 60, 40, 60] }</code></td>
</tr>
<tr>
<td><code>header</code></td>
<td>Specifies a header for the PDF. Can be a static text or dynamic content.</td>
<td><code>{ "header": "My PDF Report" }</code></td>
</tr>
<tr>
<td><code>footer</code></td>
<td>Specifies a footer for the PDF. Can be a static text or dynamic content.</td>
<td><code>{ "footer": "Page {PAGE_NUM} of {PAGE_COUNT}" }</code></td>
</tr>
<tr>
<td><code>defaultStyle</code></td>
<td>Defines the default style for all content in the PDF.</td>
<td><code>{ "defaultStyle": { "font": "Helvetica", "fontSize": 12 } }</code></td>
</tr>
<tr>
<td><code>background</code></td>
<td>Adds a background to the page or content area.</td>
<td><code>{ "background": { "image": "imageData" } }</code></td>
</tr>
<tr>
<td><code>width</code></td>
<td>Sets the width of table cells or other elements.</td>
<td><code>{ "width": 150 }</code></td>
</tr>
<tr>
<td><code>height</code></td>
<td>Sets the height of table cells or other elements.</td>
<td><code>{ "height": 50 }</code></td>
</tr>
<tr>
<td><code>alignment</code></td>
<td>Specifies the text alignment (left, center, right).</td>
<td><code>{ "alignment": "center" }</code></td>
</tr>
<tr>
<td><code>border</code></td>
<td>Defines the border for tables or table cells (style, width, and color).</td>
<td><code>{ "border": [true, true, true, true] }</code></td>
</tr>
</tbody>
</table>
<p>For additional attributes and information, refer to the <a href="https://pdfmake.github.io/docs/0.1/document-definition-object/">pdfmake documentation</a>, as pdfbuilder-node uses this library to generate PDFs.</p>
<p>Here’s a simple example of how you can use these attributes to create a basic PDF:</p>
<div style="position: relative" id="code-container-248">
<pre class="language-json"><code id="code-248" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"svg"</span><span class="token operator">:</span> <span class="token string">"logoDataHere"</span><span class="token punctuation">,</span><br /> <span class="token property">"width"</span><span class="token operator">:</span> <span class="token number">150</span><span class="token punctuation">,</span><br /> <span class="token property">"alignment"</span><span class="token operator">:</span> <span class="token string">"center"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Production Report - 2025"</span><span class="token punctuation">,</span><br /> <span class="token property">"style"</span><span class="token operator">:</span> <span class="token string">"header"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Daily Production Summary with Operator Performance"</span><span class="token punctuation">,</span><br /> <span class="token property">"style"</span><span class="token operator">:</span> <span class="token string">"subheader"</span><span class="token punctuation">,</span><br /> <span class="token property">"alignment"</span><span class="token operator">:</span> <span class="token string">"center"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"layout"</span><span class="token operator">:</span> <span class="token string">"lightHorizontalLines"</span><span class="token punctuation">,</span><br /> <span class="token property">"table"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"headerRows"</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token property">"widths"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"auto"</span><span class="token punctuation">,</span> <span class="token string">"auto"</span><span class="token punctuation">,</span> <span class="token string">"*"</span><span class="token punctuation">,</span> <span class="token string">"auto"</span><span class="token punctuation">,</span> <span class="token string">"auto"</span><span class="token punctuation">,</span> <span class="token string">"*"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"body"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><span class="token string">"Date"</span><span class="token punctuation">,</span> <span class="token string">"Shift"</span><span class="token punctuation">,</span> <span class="token string">"Product"</span><span class="token punctuation">,</span> <span class="token string">"Units Produced"</span><span class="token punctuation">,</span> <span class="token string">"Defective Units"</span><span class="token punctuation">,</span> <span class="token string">"Operator"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-01"</span><span class="token punctuation">,</span> <span class="token string">"Morning"</span><span class="token punctuation">,</span> <span class="token string">"Product A"</span><span class="token punctuation">,</span> <span class="token string">"1000"</span><span class="token punctuation">,</span> <span class="token string">"20"</span><span class="token punctuation">,</span> <span class="token string">"John Doe"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-01"</span><span class="token punctuation">,</span> <span class="token string">"Afternoon"</span><span class="token punctuation">,</span> <span class="token string">"Product B"</span><span class="token punctuation">,</span> <span class="token string">"950"</span><span class="token punctuation">,</span> <span class="token string">"15"</span><span class="token punctuation">,</span> <span class="token string">"Jane Smith"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-02"</span><span class="token punctuation">,</span> <span class="token string">"Morning"</span><span class="token punctuation">,</span> <span class="token string">"Product A"</span><span class="token punctuation">,</span> <span class="token string">"1050"</span><span class="token punctuation">,</span> <span class="token string">"10"</span><span class="token punctuation">,</span> <span class="token string">"James Brown"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-02"</span><span class="token punctuation">,</span> <span class="token string">"Afternoon"</span><span class="token punctuation">,</span> <span class="token string">"Product C"</span><span class="token punctuation">,</span> <span class="token string">"800"</span><span class="token punctuation">,</span> <span class="token string">"30"</span><span class="token punctuation">,</span> <span class="token string">"Emily Clark"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-03"</span><span class="token punctuation">,</span> <span class="token string">"Morning"</span><span class="token punctuation">,</span> <span class="token string">"Product B"</span><span class="token punctuation">,</span> <span class="token string">"1100"</span><span class="token punctuation">,</span> <span class="token string">"25"</span><span class="token punctuation">,</span> <span class="token string">"Michael Green"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token punctuation">[</span><span class="token string">"2025-01-03"</span><span class="token punctuation">,</span> <span class="token string">"Afternoon"</span><span class="token punctuation">,</span> <span class="token string">"Product A"</span><span class="token punctuation">,</span> <span class="token string">"980"</span><span class="token punctuation">,</span> <span class="token string">"18"</span><span class="token punctuation">,</span> <span class="token string">"Sarah White"</span><span class="token punctuation">]</span><br /> <span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"This table summarizes the daily production output across different shifts and operators. It includes total units produced and defective units recorded for quality analysis."</span><span class="token punctuation">,</span><br /> <span class="token property">"fontSize"</span><span class="token operator">:</span> <span class="token number">12</span><span class="token punctuation">,</span><br /> <span class="token property">"alignment"</span><span class="token operator">:</span> <span class="token string">"justify"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"text"</span><span class="token operator">:</span> <span class="token string">"Internal Use Only - Manufacturing Co."</span><span class="token punctuation">,</span><br /> <span class="token property">"style"</span><span class="token operator">:</span> <span class="token string">"footer"</span><span class="token punctuation">,</span><br /> <span class="token property">"alignment"</span><span class="token operator">:</span> <span class="token string">"center"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token property">"styles"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"header"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"fontSize"</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span><br /> <span class="token property">"bold"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token property">"alignment"</span><span class="token operator">:</span> <span class="token string">"center"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"subheader"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"fontSize"</span><span class="token operator">:</span> <span class="token number">14</span><span class="token punctuation">,</span><br /> <span class="token property">"italics"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token property">"color"</span><span class="token operator">:</span> <span class="token string">"grey"</span><span class="token punctuation">,</span><br /> <span class="token property">"margin"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"footer"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"fontSize"</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span><br /> <span class="token property">"color"</span><span class="token operator">:</span> <span class="token string">"grey"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"pageSize"</span><span class="token operator">:</span> <span class="token string">"A4"</span><span class="token punctuation">,</span><br /> <span class="token property">"pageMargins"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">40</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">,</span> <span class="token number">40</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-248" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This example creates a simple PDF featuring a centered logo, a title, a subtitle, a table with a light horizontal line layout, a paragraph of text, and a footer at the end. The following screenshot shows how it looks. You can customize it by adjusting the styles, layout, and content.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/example-pdf-ejMXbe82cl-1010.avif 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/example-pdf-ejMXbe82cl-1010.webp 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example pdf result" loading="lazy" decoding="async" src="https://flowfuse.com/img/example-pdf-ejMXbe82cl-1010.jpeg" width="1010" height="1432" /></picture>
<em>Example pdf result</em></p>
<h3 id="step-3%3A-creating-a-flow-to-generate-a-pdf" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#step-3%3A-creating-a-flow-to-generate-a-pdf"></a> Step 3: Creating a Flow to Generate a PDF</h3>
<p>Let's learn how to generate a PDF using dynamic inputs. For this, we’ll use the same example PDF report shown earlier—but this time, we’ll replace the hardcoded values with dynamic input data.</p>
<ol>
<li>For this guide's practical example, we will use the following SQLite flow that generates simulated production data. If you don't have the data source, you can import the flow below to follow along. After importing, deploy the flow and click the Inject node button to generate and insert the data.</li>
</ol>
<p>When generating PDFs for your specific data, start by creating a flow to collect the information you want in the report. This data can come from sensors, databases, APIs, or even manual inputs.</p>
<div id="nr-flow-176" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow176 = "\n[{\"id\":\"1e73fef718bb4876\",\"type\":\"group\",\"z\":\"b37428694e90b2c5\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"5169b96ad66dcff6\",\"b75fde37ea431d84\",\"a571bbd7b0c0cb25\"],\"x\":14,\"y\":59,\"w\":812,\"h\":82},{\"id\":\"5169b96ad66dcff6\",\"type\":\"inject\",\"z\":\"b37428694e90b2c5\",\"g\":\"1e73fef718bb4876\",\"name\":\"Create Table\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":130,\"y\":100,\"wires\":[[\"b75fde37ea431d84\"]]},{\"id\":\"b75fde37ea431d84\",\"type\":\"sqlite\",\"z\":\"b37428694e90b2c5\",\"g\":\"1e73fef718bb4876\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"fixed\",\"sql\":\"CREATE TABLE IF NOT EXISTS production_report (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n date TEXT NOT NULL,\\n shift TEXT NOT NULL,\\n product TEXT NOT NULL,\\n units_produced INTEGER NOT NULL,\\n defective_units INTEGER NOT NULL,\\n operator TEXT NOT NULL\\n);\",\"name\":\"\",\"x\":440,\"y\":100,\"wires\":[[\"a571bbd7b0c0cb25\"]]},{\"id\":\"a571bbd7b0c0cb25\",\"type\":\"debug\",\"z\":\"b37428694e90b2c5\",\"g\":\"1e73fef718bb4876\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":720,\"y\":100,\"wires\":[]},{\"id\":\"1ae6d7f7fdb60191\",\"type\":\"sqlitedb\",\"db\":\"productiondata.sqlite\",\"mode\":\"RWC\"},{\"id\":\"ccca7810c6b3db41\",\"type\":\"group\",\"z\":\"b37428694e90b2c5\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"19ad08d3015ef8f2\",\"b706e4aa8a2d0740\",\"f32cdc1dd16b56b7\",\"3708b00ae17defa5\",\"c4464e3454a8805e\",\"2df338e18c9a60d5\"],\"x\":14,\"y\":179,\"w\":1332,\"h\":82},{\"id\":\"19ad08d3015ef8f2\",\"type\":\"sqlite\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"prepared\",\"sql\":\"INSERT INTO production_report (\\n date,\\n shift,\\n product,\\n units_produced,\\n defective_units,\\n operator\\n) VALUES (\\n $date,\\n $shift,\\n $product,\\n $units_produced,\\n $defective_units,\\n $operator\\n);\\n\",\"name\":\"\",\"x\":1060,\"y\":220,\"wires\":[[\"2df338e18c9a60d5\"]]},{\"id\":\"b706e4aa8a2d0740\",\"type\":\"inject\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"name\":\"Click to generate and insert data\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":190,\"y\":220,\"wires\":[[\"f32cdc1dd16b56b7\"]]},{\"id\":\"f32cdc1dd16b56b7\",\"type\":\"function\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"name\":\"Generate Simulated Production Data\",\"func\":\"const products = [\\\"Widget A\\\", \\\"Widget B\\\", \\\"Gadget X\\\", \\\"Component Z\\\"];\\nconst operators = [\\\"John Matthews\\\", \\\"Sarah Lee\\\", \\\"Amit Kumar\\\", \\\"Rita Patel\\\"];\\nconst shifts = [\\\"A\\\", \\\"B\\\", \\\"C\\\"];\\n\\nfunction getRandomInt(min, max) {\\n return Math.floor(Math.random() * (max - min + 1)) + min;\\n}\\n\\nconst data = [];\\n\\nfor (let i = 0; i < 10; i++) {\\n const date = new Date();\\n date.setDate(date.getDate() - i); // Last 10 days\\n\\n data.push({\\n date: date.toISOString().split('T')[0],\\n shift: shifts[getRandomInt(0, shifts.length - 1)],\\n product: products[getRandomInt(0, products.length - 1)],\\n units_produced: getRandomInt(400, 600),\\n defective_units: getRandomInt(0, 10),\\n operator: operators[getRandomInt(0, operators.length - 1)]\\n });\\n}\\n\\nmsg.payload = data;\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":490,\"y\":220,\"wires\":[[\"3708b00ae17defa5\"]]},{\"id\":\"3708b00ae17defa5\",\"type\":\"split\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"name\":\"\",\"splt\":\"\\\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"property\":\"payload\",\"x\":710,\"y\":220,\"wires\":[[\"c4464e3454a8805e\"]]},{\"id\":\"c4464e3454a8805e\",\"type\":\"change\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"params\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"params.$date\",\"pt\":\"msg\",\"to\":\"payload.date\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$shift\",\"pt\":\"msg\",\"to\":\"payload.shift\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$product\",\"pt\":\"msg\",\"to\":\"payload.product\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$units_produced\",\"pt\":\"msg\",\"to\":\"payload.units_produced\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$defective_units\",\"pt\":\"msg\",\"to\":\"payload.defective_units\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"params.$operator\",\"pt\":\"msg\",\"to\":\"payload.operator\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":860,\"y\":220,\"wires\":[[\"19ad08d3015ef8f2\"]]},{\"id\":\"2df338e18c9a60d5\",\"type\":\"debug\",\"z\":\"b37428694e90b2c5\",\"g\":\"ccca7810c6b3db41\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1240,\"y\":220,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow176.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-176') })</script>
<ol start="3">
<li>Drag an <strong>Inject</strong> node onto the canvas.</li>
<li>Drag an <strong>SQLite</strong> node and connect it to the Inject node. Configure the <strong>SQLite</strong> node with the same database to generate the simulated data. Set the SQL Query type to "fixed statement" and use the following query:</li>
</ol>
<div style="position: relative" id="code-container-284">
<pre class="language-sql"><code id="code-284" class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> production_report<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-284" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Drag a <strong>Function</strong> node onto the canvas and paste the following JavaScript code. When generating PDFs for your specific data, make sure to adjust the code to match your data structure.</li>
</ol>
<div style="position: relative" id="code-container-292">
<pre class="language-javascript"><code id="code-292" class="language-javascript"><span class="token comment">// Initialize table body with headers</span><br /><span class="token keyword">const</span> tableBody <span class="token operator">=</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">[</span><span class="token string">'Date'</span><span class="token punctuation">,</span> <span class="token string">'Shift'</span><span class="token punctuation">,</span> <span class="token string">'Product'</span><span class="token punctuation">,</span> <span class="token string">'Units Produced'</span><span class="token punctuation">,</span> <span class="token string">'Defective Units'</span><span class="token punctuation">,</span> <span class="token string">'Operator'</span><span class="token punctuation">]</span><br /><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">const</span> logo <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><replace-this-your-logo-svg</span><span class="token template-punctuation string">`</span></span><br /><br /><span class="token comment">// Loop through data rows from SQLite (msg.payload)</span><br /><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> row <span class="token keyword">of</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> tableBody<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">[</span><br /> row<span class="token punctuation">.</span>date<span class="token punctuation">,</span><br /> row<span class="token punctuation">.</span>shift<span class="token punctuation">,</span><br /> row<span class="token punctuation">.</span>product<span class="token punctuation">,</span><br /> row<span class="token punctuation">.</span>units_produced<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> row<span class="token punctuation">.</span>defective_units<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> row<span class="token punctuation">.</span>operator<br /> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">const</span> docDefinition <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">content</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">svg</span><span class="token operator">:</span> logo<span class="token punctuation">,</span><br /> <span class="token literal-property property">width</span><span class="token operator">:</span> <span class="token number">150</span><span class="token punctuation">,</span> <span class="token comment">// Adjust the logo size as needed</span><br /> <span class="token literal-property property">alignment</span><span class="token operator">:</span> <span class="token string">'center'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Header</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Production Report - 2025'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'header'</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><br /> <span class="token comment">// Subheader</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Daily Production Summary with Operator Performance'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'subheader'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">alignment</span><span class="token operator">:</span> <span class="token string">'center'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><br /> <span class="token comment">// Table</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">layout</span><span class="token operator">:</span> <span class="token string">'lightHorizontalLines'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">table</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">headerRows</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">widths</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'auto'</span><span class="token punctuation">,</span> <span class="token string">'auto'</span><span class="token punctuation">,</span> <span class="token string">'*'</span><span class="token punctuation">,</span> <span class="token string">'auto'</span><span class="token punctuation">,</span> <span class="token string">'auto'</span><span class="token punctuation">,</span> <span class="token string">'*'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">body</span><span class="token operator">:</span> tableBody<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><br /> <span class="token comment">// Description</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'This table summarizes the daily production output across different shifts and operators. It includes total units produced and defective units recorded for quality analysis.'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">fontSize</span><span class="token operator">:</span> <span class="token number">12</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">alignment</span><span class="token operator">:</span> <span class="token string">'justify'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><br /> <span class="token comment">// Footer</span><br /> <span class="token punctuation">{</span><br /> <span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Internal Use Only - Manufacturing Co.'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">style</span><span class="token operator">:</span> <span class="token string">'footer'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">alignment</span><span class="token operator">:</span> <span class="token string">'center'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><span class="token punctuation">,</span><br /><br /> <span class="token literal-property property">styles</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">header</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">fontSize</span><span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">bold</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">alignment</span><span class="token operator">:</span> <span class="token string">'center'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">subheader</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">fontSize</span><span class="token operator">:</span> <span class="token number">14</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">italics</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">color</span><span class="token operator">:</span> <span class="token string">'grey'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">margin</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">footer</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">fontSize</span><span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">color</span><span class="token operator">:</span> <span class="token string">'grey'</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><br /> <span class="token literal-property property">pageSize</span><span class="token operator">:</span> <span class="token string">'A4'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">pageMargins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">40</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">,</span> <span class="token number">40</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">]</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> docDefinition<span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-292" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="6">
<li>Drag a <strong>pdfbuilder</strong> node onto the canvas. Set the input property to <code>msg.payload</code>, set output type to Buffer, and output property to <code>msg.payload</code>.</li>
<li>Drag a <strong>Write File</strong> node, configure it with:
<ul>
<li>Filename: test.pdf</li>
<li>Action: Overwrite file</li>
<li>Add newline (\n) to each payload?: Checked</li>
</ul>
</li>
<li>Connect the <strong>SQLite</strong> node to the <strong>Function</strong> node, then to the <strong>pdfbuilder</strong> node, and finally to the <strong>Write File</strong> node.</li>
<li>Deploy the flow and click inject node to generate the pdf.</li>
</ol>
<p>Once the PDF is generated, you can find it in the <code>.node-red</code> directory.</p>
<p>However, if you want to share the PDF with others, display it on the dashboard, and provide a download button, you can use the HTTP API, an iframe, and a few supporting nodes. Let's walk through how to do that next.</p>
<h3 id="step-4%3A-serving-the-pdf-via-http-and-previewing-it-on-the-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#step-4%3A-serving-the-pdf-via-http-and-previewing-it-on-the-dashboard"></a> Step 4: Serving the PDF via HTTP and Previewing It on the Dashboard</h3>
<p>In this step, we’ll make the generated PDF accessible through a web interface. You’ll be able to preview the PDF directly in the browser and embed it in your FlowFuse dashboard for a smooth, integrated experience. We’ll also add a download button so users can easily save the report. Instead of manually retrieving the file, we’ll create an HTTP endpoint to serve the PDF and use an iframe to display it.</p>
<h4 id="exposing-the-pdf-via-http" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#exposing-the-pdf-via-http"></a> Exposing the PDF via HTTP</h4>
<ol>
<li>
<p>Drag the ** http-in ** node onto the canvas. Set the method to 'GET' and the URL to <code>/report.pdf</code>. This will create an HTTP endpoint for retrieving the generated PDF.</p>
</li>
<li>
<p>Connect the ** http-in ** node to the ** SQLite ** node. This ensures that when a request is made to this endpoint, the necessary data is fetched from the database.</p>
</li>
<li>
<p>After the <strong>Write File</strong> node in your flow, add a <strong>Change</strong> node. Connect it to the <strong>Write File</strong> node, and configure it to set the following headers for the HTTP response:</p>
<ul>
<li>Set <code>msg.headers</code> to:</li>
</ul>
<div style="position: relative" id="code-container-369">
<pre class="language-json"><code id="code-369" class="language-json"><span class="token punctuation">{</span> <br /> 'Content-Type'<span class="token operator">:</span> 'application/pdf'<span class="token punctuation">,</span> <br /> 'Content-Disposition'<span class="token operator">:</span> 'inline; filename=<span class="token string">"report.pdf"</span>' <br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-369" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag the <strong>HTTP-response</strong> node onto the canvas and connect it to the Change node.</p>
</li>
<li>
<p>Deploy the flow</p>
</li>
</ol>
<p>Now, this will send the generated PDF as a response to the incoming HTTP request, allowing it to be previewed in the browser. You can check by entering the URL:</p>
<p><code>https://<your-instance-name>.flowfuse.cloud/report.pdf</code></p>
<h3 id="embedding-the-pdf-on-the-dashboard-and-adding-the-download-button" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#embedding-the-pdf-on-the-dashboard-and-adding-the-download-button"></a> Embedding the PDF on the Dashboard and Adding the Download Button</h3>
<p>Now, let's embed the PDF into the dashboard:</p>
<ol>
<li>
<p>Drag a <strong>ui-event</strong> node onto the canvas and configure it with the appropriate UI base settings.</p>
</li>
<li>
<p>Next, drag an <strong>iframe</strong> node onto the canvas.</p>
<ul>
<li>Select the correct group where the PDF should be displayed.</li>
<li>Adjust the size according to your preferences.</li>
<li>In the URL field, enter:</li>
</ul>
<pre><code>https://<your-instance-name>.flowfuse.cloud/report.pdf
</code></pre>
</li>
<li>
<p>Click Done and Deploy the flow.</p>
</li>
</ol>
<p>Once deployed, when you open the dashboard, the generated PDF will be embedded and displayed directly on the dashboard page.</p>
<p>Now, let's add a download button:</p>
<ol start="4">
<li>Drag a <strong>ui-template</strong> widget onto the canvas and paste the following HTML code into it:</li>
</ol>
<div style="position: relative" id="code-container-442">
<pre class="language-html"><code id="code-442" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">text-align</span><span class="token punctuation">:</span>center<span class="token punctuation">;</span> <span class="token property">margin-top</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://<your-instance-name>.flowfuse.cloud/report.pdf<span class="token punctuation">"</span></span> <span class="token attr-name">download</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>report.pdf<span class="token punctuation">"</span></span><br /> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #4f7a28<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">padding</span><span class="token punctuation">:</span> 14px 20px<span class="token punctuation">;</span> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span> <span class="token property">border-radius</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 4px 8px <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token property">transition</span><span class="token punctuation">:</span> background-color 0.3s ease<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /> Download Report<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-442" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Deploy the flow.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-with-embedded-report-6Ayi1Ig6Sz-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-with-embedded-report-6Ayi1Ig6Sz-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Dashboard displaying embedded PDF with a download button" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-with-embedded-report-6Ayi1Ig6Sz-1920.jpeg" width="1920" height="1094" /></picture>
<em>[Dashboard displaying embedded PDF with a download button]</em></p>
<h2 id="final-thought" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/05/how-to-generate-pdf-reports-using-node-red/#final-thought"></a> Final thought</h2>
<p>Automating PDF report generation in Node-RED is a great way to save time and effort. Using tools like the node-red-contrib-pdfmake node, you can quickly turn your data into well-designed PDFs without manual work. If you want to save time and avoid the setup process, you can directly use our ready-made <a href="https://flowfuse.com/blueprints/manufacturing/pdf-report-generator/">PDF generation blueprint</a>. It’s an easy way to get started and generate professional reports quickly.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/Part 3: Building an OEE Dashboard with FlowFuseBuild a responsive OEE dashboard with real-time updates using FlowFuse.2025-04-16T00:00:00ZSumit Shinde<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/">Part 2</a>, we built the flow to calculate OEE for the production line using simulated production and downtime data and created a dashboard interface for visualization. However, we did not focus much on theme, layout, or styling.</p>
<!--more-->
<p>In Part 3, we will focus on improving the theme and design of the OEE dashboard. We will learn how to connect a real data source, adjust fields if your data structure differs, scale the dashboard for multiple production lines, and finally, explore how you can use it to take action based on insights.</p>
<p>Let's get started!</p>
<h2 id="enhancing-the-dashboard-theme-and-design" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#enhancing-the-dashboard-theme-and-design"></a> Enhancing the Dashboard Theme and Design</h2>
<p>In the planning section of <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/">Part 1</a>, we introduced a mockup of the dashboard with a modern dark theme. The theme was built around a sleek, professional aesthetic, using high-contrast colors for readability and a visually appealing layout.</p>
<p>The primary colors in the theme include:</p>
<ul>
<li><strong>Black (#000000)</strong> — used for the page background to create contrast and reduce eye strain.</li>
<li><strong>Charcoal Blue (#1A1C24)</strong> — a deep, muted tone that adds depth while maintaining a clean and modern look, used for the groups.</li>
<li><strong>White (#FFFFFF)</strong> — used for text elements to ensure maximum readability against the dark background.</li>
<li><strong>Accent Colors</strong> — vibrant colors such as teal, orange, green, yellow, and blue are used across widget elements, including chart bars, line graphs, and indicators. These accents help differentiate data types and bring attention to key metrics.</li>
</ul>
<p>But how do you come up with a dashboard design like this on your own? It starts with understanding why the theme matters. The design should reflect the context it is used in, the people interacting with it, and the mood it should convey. A dashboard on a factory floor may need to feel bold and focused, while one used by executives might aim for minimal and polished. A hospital system would need a tone that is calm, clean, and highly legible.</p>
<p>If you have a brand palette, that’s a great starting point. If not, choose colors that support the usability and tone of your dashboard. Our OEE dashboard, for instance, was designed for manufacturing teams who need to quickly read live data. The layout needed to be sharp, high-contrast, and low on visual noise—ideal for control rooms with limited lighting. The dark theme helps key data stand out while reducing eye strain over long periods of use.</p>
<h3 id="modifying-theme" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#modifying-theme"></a> Modifying Theme</h3>
<ol>
<li>Open the Dashboard 2.0 sidebar from the Node-RED editor.</li>
<li>Switch to the Theme tab.</li>
<li>In the list of themes (you will likely see only the default one), click the settings (gear) icon next to it.</li>
<li>In the theme settings, click any colored rectangle to open the color picker. You can use the wheel or the dropper tool at the bottom to pick exact colors:</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/color-picking-Rn1aguJTB8-1010.avif 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/color-picking-Rn1aguJTB8-1010.webp 1010w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Color tool for selecting colors for the theme" loading="lazy" decoding="async" src="https://flowfuse.com/img/color-picking-Rn1aguJTB8-1010.jpeg" width="1010" height="1064" /></picture>
<em>Color tool for selecting colors for the theme</em></p>
<ul>
<li>Set Charcoal Blue (#1A1C24) as the color for the header background, group background, and group outline.</li>
<li>Set Black (#000000) as the page background.</li>
</ul>
<ol start="5">
<li>Click <strong>Update</strong> and <strong>Deploy Changes</strong>.</li>
</ol>
<p>Your dashboard should display the updated dark theme with a clean, modern appearance and improved visual contrast.</p>
<p>However, additional adjustments are needed to fully align the visuals, specifically the chart grid lines and label text colors.</p>
<h3 id="to-update-these%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#to-update-these%3A"></a> To update these:</h3>
<ol>
<li>Double-click on a chart widget to open its configuration panel.</li>
<li>Scroll to the bottom of the chart config UI.</li>
<li>Uncheck the following options:
<ul>
<li>Use ChartJs Default Text Colors</li>
<li>Use ChartJs Default Grid Colors</li>
</ul>
</li>
<li>Set the text color to <code>#FFFFFF</code> (white) and the grid line color to <code>#606060</code>.</li>
<li>Click <strong>Done</strong>, then <strong>Deploy the changes</strong>.</li>
</ol>
<p>These tweaks will ensure the charts match the dark theme and maintain good readability.</p>
<h2 id="improving-layout-consistency-across-screen-sizes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#improving-layout-consistency-across-screen-sizes"></a> Improving Layout Consistency Across Screen Sizes</h2>
<p>When you open the same dashboard on different screen sizes—such as a mobile phone, tablet, or smaller desktop monitor—you might find the layout inconsistent or cramped. For example, widgets may overlap or appear too small.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-breaked-layout-Jmzl3Bg44i-691.avif 691w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-breaked-layout-Jmzl3Bg44i-691.webp 691w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard broken layout on smaller screen" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-breaked-layout-Jmzl3Bg44i-691.jpeg" width="691" height="1289" /></picture>
<em>OEE Dashboard broken layout on smaller screen</em></p>
<h3 id="to-make-the-dashboard-truly-responsive%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#to-make-the-dashboard-truly-responsive%3A"></a> To make the dashboard truly responsive:</h3>
<ol>
<li>Open the <strong>Page Settings</strong> in the Node-RED Dashboard editor.</li>
<li>Scroll down to locate the <strong>Breakpoint Settings Table</strong> for different device sizes.</li>
<li>Identify the <strong>Tablet</strong> row in the table.</li>
<li>Notice that the current Tablet column count is set to <code>9</code>.</li>
</ol>
<p>Our OEE dashboard has:</p>
<ul>
<li>Four KPI widgets (OEE, Performance, Availability, Quality), each set to 3 columns wide.</li>
<li>A total of 3 × 4 = 12 columns, which does not fit in the 9-column grid—so the layout breaks, and one widget drops to the next row.</li>
<li>Other widgets like Production Summary and Downtime Events are each 6 columns wide, which leaves 3 columns of unused space in a 9-column layout.</li>
</ul>
<ol start="5">
<li>To correct this, set the Tablet column count to <code>6</code> in the breakpoint table.</li>
</ol>
<p>This change ensures:</p>
<ul>
<li>Two KPI widgets fit perfectly per row (3 + 3 = 6).</li>
<li>Summary widgets span the full row (6/6), making the layout cleaner and more consistent on tablet devices.</li>
</ul>
<ol start="6">
<li>Click Deploy the changes.</li>
</ol>
<p>Even after adjusting the breakpoint settings, one more issue may appear: inconsistent heights between the <em>Top Underperforming Machines</em> and <em>Recent Downtime Events</em> sections—especially when one of the tables has fewer rows than the other.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/table-incosistency-aq49QQFOCW-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/table-incosistency-aq49QQFOCW-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Inconsistent height of the widgets on OEE Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/table-incosistency-aq49QQFOCW-1920.jpeg" width="1920" height="1017" /></picture>
<em>Inconsistent height of the widgets on the OEE Dashboard</em></p>
<p>This can make the dashboard layout uneven, with one card appearing much shorter.</p>
<p>To fix this visual imbalance, apply custom CSS:</p>
<ol>
<li>Drag a <strong>Template</strong> widget onto your canvas.</li>
<li>Set its type to <code>CSS (all pages)</code>.</li>
<li>Paste the following CSS into the template:</li>
</ol>
<div style="position: relative" id="code-container-257">
<pre class="language-css"><code id="code-257" class="language-css"><span class="token selector">.nrdb-ui-group > .v-card</span> <span class="token punctuation">{</span><br /> <span class="token property">height</span><span class="token punctuation">:</span> 100% <span class="token important">!important</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-257" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Deploy the changes.</li>
</ol>
<h2 id="adding-header-elements%3A-logo-and-dashboard-title" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#adding-header-elements%3A-logo-and-dashboard-title"></a> Adding Header Elements: Logo and Dashboard Title</h2>
<p>To give your OEE Dashboard a professional look, add branding elements such as a company logo and a clear dashboard title. These additions improve usability and help users instantly recognize the dashboard's purpose.</p>
<ol>
<li>Drag <strong>Template</strong> widget onto the canvas.</li>
<li>Double click on it and add the following Vue code to it:</li>
</ol>
<div style="position: relative" id="code-container-283">
<pre class="language-html"><code id="code-283" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Teleport the title and logo to the #app-bar-actions area --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Teleport</span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#app-bar-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span> <span class="token property">margin-left</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">margin-right</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>OEE Dashboard<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Teleport</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Teleport</span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#app-bar-actions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span><br /> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>30px<span class="token punctuation">"</span></span><br /> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://flowfuse.com/handbook/images/logos/ff-logo--wordmark--white.png<span class="token punctuation">"</span></span><br /> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">margin-right</span><span class="token punctuation">:</span> 25px<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><br /> <span class="token punctuation">/></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Teleport</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">mounted</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>mounted <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-283" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>
<p>Update the <code>src</code> attribute in the <code><img></code> tag to your logo's path. If you are using FlowFuse, you can host your logo using the static assets service.</p>
</li>
<li>
<p>Click Deploy the changes.</p>
</li>
</ol>
<p>We are using Vue’s Teleport feature to insert a custom dashboard title and logo into the top bar of the Dashboard 2.0 layout. For more information, please read our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/">Customise theming in your FlowFuse Dashboard</a>.</p>
<p>The dashboard now looks clean, adapts well to all screen sizes, and maintains visual consistency across different UI elements. The header elements have also been enhanced to align with the overall design.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-with-proper-styling-qBeEAYhl6j-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-with-proper-styling-qBeEAYhl6j-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard with proper styling, theme" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-with-proper-styling-qBeEAYhl6j-1920.jpeg" width="1920" height="1089" /></picture>
<em>OEE Dashboard with proper styling, theme</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-with-styling-2-lrBLDpGyFc-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-with-styling-2-lrBLDpGyFc-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard with proper styling, theme" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-with-styling-2-lrBLDpGyFc-1920.jpeg" width="1920" height="1089" /></picture>
<em>OEE Dashboard with proper styling, theme</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-tablet-Tm6rhdC6TV-1736.avif 1736w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-tablet-Tm6rhdC6TV-1736.webp 1736w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard with proper styling, theme on smaller screen" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-tablet-Tm6rhdC6TV-1736.jpeg" width="1736" height="1574" /></picture>
<em>OEE Dashboard with proper styling, theme on smaller screen</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-tablet-O0H3eedyKY-1728.avif 1728w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-tablet-O0H3eedyKY-1728.webp 1728w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard with proper styling, theme on smaller screen" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-tablet-O0H3eedyKY-1728.jpeg" width="1728" height="1568" /></picture>
<em>OEE Dashboard with proper styling, theme on smaller screen</em></p>
<h2 id="scaling-the-dashboard-for-multiple-production-lines" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#scaling-the-dashboard-for-multiple-production-lines"></a> Scaling the Dashboard for Multiple Production Lines</h2>
<p>Currently, the dashboard is configured for a single production line. To support multiple lines, you must adjust your flows and dashboard structure to handle each line separately while keeping a consistent layout and theme.</p>
<h3 id="follow-these-steps%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#follow-these-steps%3A"></a> Follow these steps:</h3>
<ol>
<li>Select the dashboard flow that handles your current production line. Include all relevant change nodes that set values like <code>msg.quality</code>, <code>msg.performance</code>, etc., to <code>msg.payload</code>.</li>
<li>From the main menu, hover over <strong>Subflows</strong> and click <strong>Create Subflow</strong>.</li>
<li>Inside the subflow:
<ul>
<li>Add an <strong>Input</strong> node, and connect it to all the change nodes you included from the original flow.</li>
<li>Reconnect any <strong>Link In</strong> node (that was previously wired to the change nodes) to the input of the newly created subflow.</li>
</ul>
</li>
<li>Open the <strong>subflow properties</strong>, and define environment variables to represent widget groups. In the dashboard widgets inside the subflow, reference these variables instead of hardcoding group names.</li>
<li>Click <strong>Deploy</strong> to apply the changes.</li>
</ol>
<p>This modular approach simplifies scaling and reduces manual work when adding new production lines to the dashboard.</p>
<h3 id="to-reuse-it-for-another-production-line%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#to-reuse-it-for-another-production-line%3A"></a> To reuse it for another production line:</h3>
<ol>
<li>Copy the entire OEE dashboard flow.</li>
<li>Create a new tab.</li>
<li>Paste the copied flow into the new tab.</li>
<li>Rename the tab to match the new production line.</li>
<li>Create a new dashboard page with the same configuration but a different name and path.</li>
<li>Open the subflow by double-clicking on it and add a new group for dashboard widgets.</li>
<li>Go to the configuration flow, and update the line's name to match the new production line.</li>
<li>Adjust both shift duration values to reflect the new line’s schedule.</li>
<li>Click <strong>Deploy</strong>.</li>
</ol>
<p>Once deployed, you will have a separate page ready for your new production line. You can create as many pages as needed to monitor multiple production lines.</p>
<h2 id="connecting-your-real-data-source" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#connecting-your-real-data-source"></a> Connecting Your Real Data Source</h2>
<p>Now that you have built a complete OEE dashboard using simulated factory data and learned how to reuse it for all your production lines, the next step is to connect it to your real factory environment.</p>
<p>To make the dashboard truly useful in a live setting, you must understand how to integrate it with your data sources. Most commonly, the OEE dashboard relies on static or retained data, such as values stored in a database. First, determine whether your factory uses a relational database like MySQL or PostgreSQL, a NoSQL database like MongoDB, or a time-series database like InfluxDB.</p>
<h3 id="then%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#then%3A"></a> Then:</h3>
<ol>
<li>Use the <strong>Palette Manager</strong> in Node-RED to install the corresponding contrib node for your selected database.</li>
<li>Replace the existing <code>sqlite</code> nodes in your flow with the nodes for the database you are using.</li>
<li>If using SQL based database, queries may remain unchanged. For NoSQL or time-series DBs, rewrite the queries as needed.</li>
</ol>
<p>For help, refer to our <a href="https://flowfuse.com/node-red/database/">Database</a> section, which includes guides for MongoDB, PostgreSQL, InfluxDB, TimescaleDB, and DynamoDB.</p>
<p>When connecting to your real data source, you may notice that the field names used in your database differ from those used in the our oee dashboard sqlite node queries. While this seems like a lot of manual work, the dashboard is designed with flexibility in mind. You only need to make two changes to adapt the queries to your schema.</p>
<h3 id="to-match-your-schema%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#to-match-your-schema%3A"></a> To match your schema:</h3>
<ol>
<li>Open each database node and update the query to reflect your field names.
<ul>
<li>Do <strong>not</strong> change the alias names — they are used throughout the dashboard.</li>
</ul>
</li>
<li>Replace table names with those used in your actual database.</li>
<li>Do <strong>not</strong> change the dynamic parameters like <code>$startTime</code>, <code>$endTime</code>, and <code>$line</code>.</li>
</ol>
<p>Example query:</p>
<div style="position: relative" id="code-container-483">
<pre class="language-sql"><code id="code-483" class="language-sql"><span class="token keyword">SELECT</span><br /> <span class="token keyword">timestamp</span> <span class="token keyword">as</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span><br /> machine_name <span class="token keyword">as</span> machine_name<span class="token punctuation">,</span><br /> area <span class="token keyword">as</span> area<span class="token punctuation">,</span><br /> line <span class="token keyword">as</span> line<span class="token punctuation">,</span><br /> total_produced_units <span class="token keyword">as</span> total_produced_units<span class="token punctuation">,</span><br /> good_units <span class="token keyword">as</span> good_units<span class="token punctuation">,</span><br /> defect_units <span class="token keyword">as</span> defect_units<span class="token punctuation">,</span><br /> target_output <span class="token keyword">as</span> target_output<br /><span class="token keyword">FROM</span> ProductionData<br /><span class="token keyword">WHERE</span><br /> <span class="token keyword">timestamp</span> <span class="token operator">BETWEEN</span> $startTime <span class="token operator">AND</span> $endTime <span class="token operator">AND</span> line <span class="token operator">=</span> $line<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-483" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Suppose your database uses different field names, such as time instead of timestamp, machine instead of machine_name, section instead of area, production_line instead of line, produced_units instead of total_produced_units, quality_units instead of good_units, faulty_units instead of defect_units, or planned_output instead of target_output. In that case, you should update the query accordingly. After modification, it should look like this:</p>
<div style="position: relative" id="code-container-487">
<pre class="language-sql"><code id="code-487" class="language-sql"><span class="token keyword">SELECT</span><br /> <span class="token keyword">time</span> <span class="token keyword">AS</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span><br /> machine <span class="token keyword">AS</span> machine_name<span class="token punctuation">,</span><br /> section <span class="token keyword">AS</span> area<span class="token punctuation">,</span><br /> production_line <span class="token keyword">AS</span> line<span class="token punctuation">,</span><br /> produced_units <span class="token keyword">AS</span> total_produced_units<span class="token punctuation">,</span><br /> quality_units <span class="token keyword">AS</span> good_units<span class="token punctuation">,</span><br /> faulty_units <span class="token keyword">AS</span> defect_units<span class="token punctuation">,</span><br /> planned_output <span class="token keyword">AS</span> target_output<br /><span class="token keyword">FROM</span> YourTableName <br /><span class="token keyword">WHERE</span> <br /> <span class="token keyword">time</span> <span class="token operator">BETWEEN</span> $startTime <span class="token operator">AND</span> $endTime <span class="token operator">AND</span> production_line <span class="token operator">=</span> $line<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-487" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="how-to-use-your-oee-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#how-to-use-your-oee-dashboard"></a> How to Use Your OEE Dashboard</h2>
<p>Your OEE dashboard is live. It updates in real-time and shows key metrics. But what should you do with the information?</p>
<p>The dashboard is not just for display—it is there to help you take action. When OEE drops, do not stop at the number. Dig into the cause by checking the three main metrics: availability, performance, and quality.</p>
<ul>
<li>If availability is low, check for unplanned stops, long changeovers, or idle machines.</li>
<li>If performance is down, the line may run slower than expected.</li>
<li>If quality has dropped, you may produce more rejects or rework.</li>
</ul>
<p>Say your OEE drops from 82% to 65%, and performance is the problem. Start by checking how many good parts were produced. Look at reject counts—more bad parts affect both quality and output. Then, check downtime logs and machine performance. One or two machines are often behind the drop—maybe they had repeated issues or ran slowly after a setup.</p>
<p>Use the 30-day trend graph to spot patterns over time. A sudden drop might show a specific issue, while a slow decline could signal a more significant process problem. Trends can also help you confirm if recent changes are making a real difference.
Finally, share what you find. Use the dashboard during team reviews or shift handovers to keep everyone focused on what needs fixing. An OEE dashboard's real value is how you respond to it.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-3/#conclusion"></a> Conclusion</h2>
<p>This final part completes our series on building a real-time OEE dashboard with FlowFuse. You now have a fully functional, visually refined, and scalable dashboard that connects to live production data, adapts to multiple lines, and reflects your plant’s branding and layout requirements.</p>
<p>By the end of this journey, you have built a dashboard and created a foundation for continuous improvement in your manufacturing environment using open-source, low-code tools.
We hope this series helped you understand how FlowFuse and Node-RED can quickly prototype and deploy powerful industrial applications. Thank you for following along!</p>
<p>Suppose you have not built your OEE dashboard yet or are facing issues. In that case, you can get started instantly—<a href="https://app.flowfuse.com/account/create">sign up</a> now and use our ready-made <a href="https://flowfuse.com/blueprints/manufacturing/oee-dashboard/">OEE Dashboard Blueprint</a> to accelerate your deployment.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/FlowFuse 2.16: Git Integration, improved log retention and moreStart pushing your snapshots to git, get alerted when resources are running low and more logging from Node-RED2025-04-10T00:00:00ZNick O'Leary<p>Another release from the FlowFuse team to keep <a href="https://flowfuse.com/handbook/company/strategy/">realising our mission</a> to empower you to fuse the digital realm and physical reality.</p>
<!--more-->
<h2 id="git-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#git-integration"></a> Git Integration</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/git-pipeline-stage-ZZn_7KU2uY-1332.avif 1332w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/git-pipeline-stage-ZZn_7KU2uY-1332.webp 1332w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of a Git Pipeline Stage" loading="lazy" decoding="async" src="https://flowfuse.com/img/git-pipeline-stage-ZZn_7KU2uY-1332.jpeg" width="1332" height="598" /></picture>
<em>Screenshot of a Git Pipeline Stage</em></p>
<p>This has been one of those features that has come up a number of times with our users; we knew we wanted to have Git integration in the platform, but we also wanted it to fit in a natural way with the developer workflows we provide.</p>
<p>With this release, you can now add a Git Repository stage to your deployment pipelines. When the pipeline is triggered, the latest snapshot will get pushed to the configured repository.</p>
<p>This is very much a "first-iteration" of the feature that will allow us to get feedback early and continue to iterate.</p>
<p>With this release, the following restrictions apply:</p>
<ul>
<li>Only GitHub.com hosted repostories are supported</li>
<li>Users must create a GitHub Personal Access Token and add to their Team Settings</li>
<li>We currently only support pushing snapshots to a Git repository</li>
</ul>
<p>This last point is important; this release lets you backup your flows to git, but we haven't yet enabled the return journey of pulling a snapshot from a Git repository back to your Node-RED instance. That'll come in the near future and will unlock a full git-based review workflow within the pipelines.</p>
<p>We'll also look at enabling other Git hosting providers - let us know which you'd like to see on the list.</p>
<p>This feature is available to Enterprise teams on FlowFuse Cloud and self-hosted customers.</p>
<h2 id="better-node-red-log-handling" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#better-node-red-log-handling"></a> Better Node-RED log handling</h2>
<p>We've improved the log handling within our Hosted Node-RED instances. Previously we were using a fix sized buffer in memory; meaning the noisier your Node-RED instance was, the less history you'd have. The UI for browsing the logs was also awkward when you wanted to jump back to an earlier section of the logs.</p>
<p>With this release, once you update your instances to the latest version, we will
now store the last 7 days worth of logs for each hosted Node-RED instance.</p>
<p>To go along side this, we've added the ability to jump to a specific time/date in the logs without having to endlessly scroll.</p>
<p>To start benefiting from the extended logs, make sure you update the latest version via your Instance Settings page.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/browse-logs-LYAqkTpHty-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/browse-logs-LYAqkTpHty-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of browsing Node-RED logs by timestamp" loading="lazy" decoding="async" src="https://flowfuse.com/img/browse-logs-LYAqkTpHty-1920.jpeg" width="1920" height="1039" /></picture>
<em>Screenshot of browsing Node-RED logs by timestamp</em></p>
<h2 id="remote-instance-provisioning" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#remote-instance-provisioning"></a> Remote Instance Provisioning</h2>
<p>We've updated the provisioning token support to allow you to automatically assign your remote instances to an application within your team.</p>
<p>The option to assign to a hosted instance is still there, but being able to assign to the appilcation is more generally useful for most workflows on the platform.</p>
<p>Details available in the <a href="https://flowfuse.com/changelog/2025/04/device-provisioning/">changelog</a>.</p>
<h2 id="local-login-for-remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#local-login-for-remote-instances"></a> Local Login for Remote Instances</h2>
<p>One of the great features of our Remote Instance management is that we enable secure remote access to the Node-RED editor through the platform. To date, this approach has meant we disable direct local access to the editor.</p>
<p>With the most recent Device Agent release, we've added the ability to configure a local user login for the editor. This can be used when the remote instance is not able to reach the FlowFuse platform.</p>
<p>We're keen for feedback on this and will continue to explore otherwise to provide secure local access to the remote instance.</p>
<h2 id="resource-alerts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#resource-alerts"></a> Resource Alerts</h2>
<p>We expanded the notficiations we send to help you track the health of your Node-RED instances. We will now send notifications if the CPU and/or memory usages exceeds 75% of the available capacity for a prolonged time.</p>
<p>You can further opt-in or out of these notifications via the instance settings.</p>
<p>Details available in the <a href="https://flowfuse.com/changelog/2025/03/resource-notifications/">changelog</a></p>
<h2 id="changes-to-tags-for-flowfuse%2Fnode-red-containers" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#changes-to-tags-for-flowfuse%2Fnode-red-containers"></a> Changes to tags for <code>flowfuse/node-red</code> containers</h2>
<p>For kubernetes and docker environments, we've updated the base container our <code>latest</code> tag points at to ensure
it defaults to the latest Node.js and Node-RED versions.</p>
<p>Check the <a href="https://flowfuse.com/changelog/2025/03/container-tags/">changelog</a> entry for full details</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#what-else-is-new%3F"></a> What Else Is New?</h2>
<p>For a full list of everything that went into our 2.16 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.16.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/flowfuse-release-2-16/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/Part 2: Building an OEE Dashboard with FlowFuseStep-by-step guide to building your own OEE Dashboard.2025-04-09T00:00:00ZSumit Shinde<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/">Part 1</a>, we explored the fundamentals of OEE, outlined a basic design of the dashboard, and identified the key elements to include in the OEE dashboard.
In this Part 2, we will focus on building the OEE dashboard interface using <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a> (Node-RED Dashboard 2.0) and FlowFuse, utilizing simulated production and downtime data.</p>
<!--more-->
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#getting-started"></a> Getting Started</h2>
<p>To simplify the development process, we will divide development into five key parts:</p>
<ol>
<li>Collecting and configuring data</li>
<li>Preparing data for calculations</li>
<li>Calculating OEE and key metrics</li>
<li>Detailed breakdown of OEE data</li>
<li>Building the dashboard</li>
</ol>
<p>Before we start, it is recommended to have a basic knowledge of Node-RED. For that, I recommend this free <a href="https://node-red-academy.learnworlds.com/course/node-red-getting-started">Node-RED Fundamental Course</a>.</p>
<p>Additionally, ensure that you organize flows into well-structured groups. To match my group organization, I have provided images of the flow for each section. Also, if a Link In node is present at the start, create the group starting from the Link In node and ending at the Link Out node.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#prerequisites"></a> Prerequisites</h3>
<p>Before you begin building the OEE Dashboard with FlowFuse, make sure you have the following:</p>
<ul>
<li><strong>Running FlowFuse Instance:</strong> Make sure you have a FlowFuse instance set up and running. If you don't have an account, check out our <a href="https://app.flowfuse.com/account/create">free trial</a> and learn how to create an instance in FlowFuse.</li>
<li><strong>FlowFuse Dashboard:</strong> Ensure you have <a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">FlowFuse Dashboard</a> (also known as Node-RED Dashboard 2.0 in the community) installed and properly configured on your instance.</li>
<li><strong>SQLite Contrib Node:</strong> Ensure you have <a href="https://flows.nodered.org/node/node-red-node-sqlite">node-red-contrib-sqlite</a> installed.</li>
</ul>
<h3 id="preparing-simulated-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#preparing-simulated-data"></a> Preparing Simulated Data</h3>
<p>Before building the dashboard, we need a data source for production and downtime metrics. This data will serve as input for OEE calculations. We will focus on connecting a real source in the next part, but for now, let's generate simulated data.</p>
<div id="nr-flow-194" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow194 = "\n[{\"id\":\"fa7147e04d4d5ec3\",\"type\":\"tab\",\"label\":\"Simulated Data Generation\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"3f2126c3c00b9e0d\",\"type\":\"group\",\"z\":\"fa7147e04d4d5ec3\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"853fb3a395d833bb\",\"a96ffd171bf11823\",\"3d30995a4329eb71\",\"0f466ba2a885e22a\",\"f8229c651706b162\",\"2ceebc93d54adba4\",\"0c927abd3b139e97\",\"8222855bc32e9240\"],\"x\":24,\"y\":99,\"w\":1142,\"h\":182},{\"id\":\"07a3d5b9075ff846\",\"type\":\"group\",\"z\":\"fa7147e04d4d5ec3\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"aa7bd867fa7daca5\",\"234aef8a999cb8d9\",\"d7641c9327f295f8\",\"c3e96b73cd6ec586\",\"13f2e851e3f28ec6\",\"9a444655c076d5a7\",\"e1fe1b1a4f1ef2e5\",\"2beb68abc5da93bc\",\"b83d517fae3c7bbf\",\"50ebbbfb159a3cbe\",\"8873774ad339b67e\",\"77e22b20d1862c7e\"],\"x\":-6,\"y\":299,\"w\":2092,\"h\":162},{\"id\":\"c0550db87d6fb947\",\"type\":\"group\",\"z\":\"fa7147e04d4d5ec3\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"b1f773f4cc2266c6\",\"cce91928076adb23\",\"d853a554ecd152b1\",\"45628fb0ca0a4672\",\"36ae627ad694fc09\",\"69afd49e2ceb191b\",\"808db5612ba1aedc\",\"6dcd69acd5990793\"],\"x\":34,\"y\":479,\"w\":1202,\"h\":142},{\"id\":\"a96ffd171bf11823\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"Create ProductionData table\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"sql\",\"syntax\":\"mustache\",\"template\":\"CREATE TABLE IF NOT EXISTS ProductionData (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n timestamp DATETIME NOT NULL,\\n area VARCHAR(255) NOT NULL,\\n line VARCHAR(100) NOT NULL, \\n machine_name VARCHAR(255) NOT NULL,\\n shift VARCHAR(50) NOT NULL,\\n shift_duration DECIMAL(5,2) NOT NULL,\\n good_units INT NOT NULL,\\n defect_units INT NOT NULL,\\n total_produced_units INT NOT NULL,\\n cycle_time DECIMAL(5,2) NOT NULL,\\n ideal_cycle_time DECIMAL(5,2) NOT NULL,\\n target_output INT NOT NULL DEFAULT 0,\\n operating_time INT NOT NULL\\n);\\n\",\"output\":\"str\",\"x\":500,\"y\":140,\"wires\":[[\"853fb3a395d833bb\"]]},{\"id\":\"3d30995a4329eb71\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":150,\"y\":140,\"wires\":[[\"a96ffd171bf11823\"]]},{\"id\":\"0f466ba2a885e22a\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"debug 1\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1050,\"y\":140,\"wires\":[]},{\"id\":\"2ceebc93d54adba4\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"Create Downtime table\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"sql\",\"syntax\":\"mustache\",\"template\":\"CREATE TABLE IF NOT EXISTS DowntimeData (\\n id INTEGER PRIMARY KEY AUTOINCREMENT,\\n timestamp DATETIME NOT NULL,\\n area VARCHAR(255) NOT NULL,\\n line VARCHAR(100) NOT NULL, \\n machine_name VARCHAR(255) NOT NULL,\\n shift VARCHAR(50) NOT NULL,\\n downtime_start DATETIME NOT NULL,\\n downtime_end DATETIME NOT NULL,\\n downtime_duration_minutes INTEGER NOT NULL,\\n downtime_type VARCHAR(50) NOT NULL CHECK (downtime_type IN ('Planned', 'Unplanned')),\\n downtime_reason TEXT NOT NULL\\n);\\n\",\"output\":\"str\",\"x\":440,\"y\":240,\"wires\":[[\"f8229c651706b162\"]]},{\"id\":\"0c927abd3b139e97\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":150,\"y\":240,\"wires\":[[\"2ceebc93d54adba4\"]]},{\"id\":\"8222855bc32e9240\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"name\":\"debug 2\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1050,\"y\":240,\"wires\":[]},{\"id\":\"234aef8a999cb8d9\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"Insert production data record\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"INSERT INTO ProductionData (\\n timestamp, \\n area, \\n line, \\n machine_name, \\n shift, \\n shift_duration, \\n good_units, \\n defect_units, \\n total_produced_units, \\n cycle_time, \\n ideal_cycle_time, \\n target_output,\\n operating_time \\n) \\nVALUES (\\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '', \\n '' \\n);\\n\",\"output\":\"str\",\"x\":1550,\"y\":340,\"wires\":[[\"aa7bd867fa7daca5\"]]},{\"id\":\"d7641c9327f295f8\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"debug 3\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1970,\"y\":340,\"wires\":[]},{\"id\":\"cce91928076adb23\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"Drop demo downtime data\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"sql\",\"syntax\":\"mustache\",\"template\":\"Drop table DowntimeData;\",\"output\":\"str\",\"x\":480,\"y\":580,\"wires\":[[\"b1f773f4cc2266c6\"]]},{\"id\":\"d853a554ecd152b1\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":150,\"y\":580,\"wires\":[[\"cce91928076adb23\"]]},{\"id\":\"45628fb0ca0a4672\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"debug 5\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1120,\"y\":580,\"wires\":[]},{\"id\":\"c3e96b73cd6ec586\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"Click to generate and insert demo data.\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":\"0.5\",\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":230,\"y\":380,\"wires\":[[\"13f2e851e3f28ec6\"]]},{\"id\":\"13f2e851e3f28ec6\",\"type\":\"function\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"Generate simulated production and downtime data\",\"func\":\"function generateProductionData() {\\n const areas = {\\n \\\"Pressing\\\": [\\\"Hydraulic Press\\\", \\\"CNC Press Brake\\\", \\\"Stamping Press\\\", \\\"Power Press\\\"],\\n \\\"Assembly\\\": [\\\"Robotic Arm\\\", \\\"Screw Insertion Machine\\\", \\\"Pick-and-Place Machine\\\"],\\n \\\"Packaging\\\": [\\\"Carton Sealing Machine\\\", \\\"Shrink Wrapping Machine\\\", \\\"Bottle Filling and Capping Machine\\\", \\\"Flow Wrapper\\\"]\\n };\\n\\n const shifts = [\\\"Shift 1\\\", \\\"Shift 2\\\", \\\"Shift 3\\\"];\\n const shiftStartTimes = {\\n \\\"Shift 1\\\": \\\"00:00:00\\\",\\n \\\"Shift 2\\\": \\\"08:00:00\\\",\\n \\\"Shift 3\\\": \\\"16:00:00\\\"\\n };\\n\\n const uniqueShortCodes = {\\n \\\"Pressing\\\": [\\\"HP\\\", \\\"PB\\\", \\\"SP\\\", \\\"PP\\\"],\\n \\\"Assembly\\\": [\\\"RA\\\", \\\"SIM\\\", \\\"P and P\\\"],\\n \\\"Packaging\\\": [\\\"CSM\\\", \\\"SWM\\\", \\\"BFCM\\\", \\\"FW\\\"]\\n };\\n\\n const downtimeTypes = [\\\"Planned\\\", \\\"Unplanned\\\"];\\n const plannedDowntimeReasons = [\\n \\\"Scheduled Maintenance\\\", \\\"Equipment Upgrades\\\", \\\"Shift Changeover\\\",\\n \\\"Tool Changeover\\\", \\\"Calibration and Quality Checks\\\", \\\"Cleaning and Sanitation\\\", \\\"Planned Power Outage\\\"\\n ];\\n const unplannedDowntimeReasons = [\\n \\\"Power Failure\\\", \\\"Material Shortage\\\", \\\"Technical Fault\\\", \\\"Operator Unavailable\\\"\\n ];\\n\\n const data = [];\\n const downtimeData = [];\\n const now = new Date();\\n\\n const normalizeTimestamp = (timestamp) => timestamp.toISOString().split(\\\"T\\\").join(\\\" \\\").slice(0, 19);\\n\\n for (let i = 0; i < 30; i++) {\\n const date = new Date();\\n date.setDate(now.getDate() - i);\\n const formattedDate = date.toISOString().split(\\\"T\\\")[0];\\n\\n shifts.forEach(shift => {\\n const shiftStart = shiftStartTimes[shift];\\n const shiftTimestamp = new Date(`${formattedDate}T${shiftStart}`);\\n\\n Object.entries(areas).forEach(([area, machines]) => {\\n machines.forEach((_, index) => {\\n const randomCode = uniqueShortCodes[area][Math.floor(Math.random() * uniqueShortCodes[area].length)];\\n const uniqueID = Math.floor(10000 + Math.random() * 90000);\\n const shortMachineName = `${randomCode}-${uniqueID}`;\\n\\n const shiftDuration = 8;\\n let totalDowntimeMinutes = 0;\\n let downtimeEvents = [];\\n\\n // Increase downtime frequency (up to 4 events per shift)\\n const numDowntimes = Math.floor(Math.random() * 4) + 1;\\n\\n for (let d = 0; d < numDowntimes; d++) {\\n if (Math.random() < 0.75) { // Increase probability of downtime\\n let downtimeMinutes = Math.floor(5 + Math.random() * 10); // Shorter downtimes (5-15 mins)\\n totalDowntimeMinutes += downtimeMinutes;\\n\\n const downtimeStartMinutes = Math.floor(Math.random() * (shiftDuration * 60 - downtimeMinutes));\\n const downtimeStart = new Date(shiftTimestamp);\\n downtimeStart.setMinutes(downtimeStart.getMinutes() + downtimeStartMinutes);\\n\\n const downtimeEnd = new Date(downtimeStart);\\n downtimeEnd.setMinutes(downtimeEnd.getMinutes() + downtimeMinutes);\\n\\n const downtimeType = downtimeTypes[Math.floor(Math.random() * downtimeTypes.length)];\\n const downtimeReason = downtimeType === \\\"Planned\\\"\\n ? plannedDowntimeReasons[Math.floor(Math.random() * plannedDowntimeReasons.length)]\\n : unplannedDowntimeReasons[Math.floor(Math.random() * unplannedDowntimeReasons.length)];\\n\\n downtimeEvents.push({\\n timestamp: normalizeTimestamp(shiftTimestamp),\\n area,\\n machine_name: shortMachineName,\\n shift,\\n downtime_start: normalizeTimestamp(downtimeStart),\\n downtime_end: normalizeTimestamp(downtimeEnd),\\n downtime_duration_minutes: downtimeMinutes,\\n downtime_reason: downtimeReason,\\n downtime_type: downtimeType,\\n line: `Line-${index + 1}`\\n });\\n }\\n }\\n\\n const operatingTime = (shiftDuration * 60 - totalDowntimeMinutes) * 60;\\n let efficiency = Math.random() * 0.2 + 0.75;\\n let targetOutput = Math.floor(operatingTime / 3) || 1;\\n let totalProduced = Math.floor(targetOutput * efficiency);\\n totalProduced = Math.min(totalProduced, targetOutput);\\n\\n const defectRate = Math.random() * 0.02 + 0.01;\\n const defectUnits = Math.floor(totalProduced * defectRate);\\n const goodUnits = totalProduced - defectUnits;\\n\\n data.push({\\n timestamp: normalizeTimestamp(shiftTimestamp),\\n area,\\n machine_name: shortMachineName,\\n shift,\\n shift_duration: shiftDuration,\\n operating_time: operatingTime - totalDowntimeMinutes * 60,\\n good_units: goodUnits,\\n defect_units: defectUnits,\\n total_produced_units: totalProduced,\\n target_output: targetOutput,\\n line: `Line-${index + 1}`\\n });\\n\\n downtimeData.push(...downtimeEvents);\\n });\\n });\\n });\\n }\\n\\n return [\\n { payload: data },\\n { payload: downtimeData }\\n ];\\n}\\n\\nconst productionDataSet = generateProductionData();\\n\\nreturn [\\n { payload: productionDataSet[0].payload },\\n { payload: productionDataSet[1].payload }\\n];\\n\",\"outputs\":2,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":690,\"y\":380,\"wires\":[[\"9a444655c076d5a7\"],[\"8873774ad339b67e\"]]},{\"id\":\"9a444655c076d5a7\",\"type\":\"split\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"\",\"splt\":\"\\\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":1060,\"y\":340,\"wires\":[[\"e1fe1b1a4f1ef2e5\"]]},{\"id\":\"e1fe1b1a4f1ef2e5\",\"type\":\"delay\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"10 msg/s\",\"pauseType\":\"rate\",\"timeout\":\"5\",\"timeoutUnits\":\"seconds\",\"rate\":\"10\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"1\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":1280,\"y\":340,\"wires\":[[\"234aef8a999cb8d9\"]]},{\"id\":\"b83d517fae3c7bbf\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"Insert downtime data record\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"INSERT INTO DowntimeData ( timestamp, area, line, machine_name, shift, \\n downtime_start, downtime_end, downtime_duration_minutes, \\n downtime_type, downtime_reason\\n) \\nVALUES (\\n '', \\n '', \\n '', \\n '', \\n '', \\n '',\\n '', \\n , \\n '', \\n '' \\n);\\n\",\"output\":\"str\",\"x\":1550,\"y\":420,\"wires\":[[\"2beb68abc5da93bc\"]]},{\"id\":\"50ebbbfb159a3cbe\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"debug 8\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1970,\"y\":420,\"wires\":[]},{\"id\":\"8873774ad339b67e\",\"type\":\"split\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"\",\"splt\":\"\\\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":1060,\"y\":420,\"wires\":[[\"77e22b20d1862c7e\"]]},{\"id\":\"77e22b20d1862c7e\",\"type\":\"delay\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"name\":\"10 msg/s\",\"pauseType\":\"rate\",\"timeout\":\"5\",\"timeoutUnits\":\"seconds\",\"rate\":\"10\",\"nbRateUnits\":\"1\",\"rateUnits\":\"second\",\"randomFirst\":\"1\",\"randomLast\":\"5\",\"randomUnits\":\"seconds\",\"drop\":false,\"allowrate\":false,\"outputs\":1,\"x\":1280,\"y\":420,\"wires\":[[\"b83d517fae3c7bbf\"]]},{\"id\":\"69afd49e2ceb191b\",\"type\":\"template\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"Drop demo production data\",\"field\":\"topic\",\"fieldType\":\"msg\",\"format\":\"sql\",\"syntax\":\"mustache\",\"template\":\"Drop table ProductionData;\",\"output\":\"str\",\"x\":480,\"y\":520,\"wires\":[[\"36ae627ad694fc09\"]]},{\"id\":\"808db5612ba1aedc\",\"type\":\"inject\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":150,\"y\":520,\"wires\":[[\"69afd49e2ceb191b\"]]},{\"id\":\"6dcd69acd5990793\",\"type\":\"debug\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"name\":\"debug 4\",\"active\":false,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1120,\"y\":520,\"wires\":[]},{\"id\":\"853fb3a395d833bb\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":720,\"y\":140,\"wires\":[[\"0f466ba2a885e22a\"]]},{\"id\":\"f8229c651706b162\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"3f2126c3c00b9e0d\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":720,\"y\":240,\"wires\":[[\"8222855bc32e9240\"]]},{\"id\":\"aa7bd867fa7daca5\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":1780,\"y\":340,\"wires\":[[\"d7641c9327f295f8\"]]},{\"id\":\"b1f773f4cc2266c6\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":730,\"y\":580,\"wires\":[[\"45628fb0ca0a4672\"]]},{\"id\":\"2beb68abc5da93bc\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"07a3d5b9075ff846\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":1780,\"y\":420,\"wires\":[[\"50ebbbfb159a3cbe\"]]},{\"id\":\"36ae627ad694fc09\",\"type\":\"sqlite\",\"z\":\"fa7147e04d4d5ec3\",\"g\":\"c0550db87d6fb947\",\"mydb\":\"1ae6d7f7fdb60191\",\"sqlquery\":\"msg.topic\",\"sql\":\"\",\"name\":\"\",\"x\":730,\"y\":520,\"wires\":[[\"6dcd69acd5990793\"]]},{\"id\":\"1ae6d7f7fdb60191\",\"type\":\"sqlitedb\",\"db\":\"sqllite\",\"mode\":\"RWC\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow194.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-194') })</script>
<ol>
<li>
<p>Import the provided flow for data generation.</p>
</li>
<li>
<p>Click the <strong>Deploy</strong> button to activate the flow.</p>
</li>
<li>
<p>On deployment, it will create two SQLite tables: <code>ProductionData</code> and <code>DowntimeData</code>.</p>
</li>
<li>
<p>Find the <strong>Inject node</strong> labeled <em>Click to generate and insert demo data</em>.</p>
</li>
<li>
<p>Click the inject node to trigger data generation.</p>
</li>
</ol>
<p>The flow will generate data with the following fields:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/demo-data-props-aqhGR3WqKP-944.avif 944w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/demo-data-props-aqhGR3WqKP-944.webp 944w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Demo Production and Downtime data object" loading="lazy" decoding="async" src="https://flowfuse.com/img/demo-data-props-aqhGR3WqKP-944.jpeg" width="944" height="326" /></picture>
<em>Demo Production and Downtime data object</em></p>
<h3 id="collecting-and-configuring-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#collecting-and-configuring-data"></a> Collecting and Configuring Data</h3>
<p>Once the simulated data is generated and stored in SQLite, the next step is to create a flow for configuration settings. These settings will be used across the entire flow, allowing the flow to be reused by simply modifying the settings. The configured data will then be collected for use in the OEE dashboard.</p>
<h4 id="adding-flow-to-configure-settings%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#adding-flow-to-configure-settings%3A"></a> Adding flow to configure settings:</h4>
<ol>
<li>
<p>Click on the <strong>"+"</strong> to create a new flow.</p>
</li>
<li>
<p>Name the newly created flow to <strong>OEE Dashboard for Line-1</strong>.</p>
</li>
<li>
<p>Drag a <strong>Change node</strong> onto the canvas, double-click it, and add the following elements:</p>
<ul>
<li>Set <code>flow.line</code> to <code>"Line-1"</code></li>
<li>Set <code>flow.shift_duration</code> to <code>12</code></li>
<li>Set <code>flow.shiftDuration24h</code> to <code>24</code></li>
</ul>
</li>
<li>
<p>Drag an <strong>Inject node</strong>, set it to trigger on deploy by enabling <strong>Inject once after X seconds</strong> (set delay to <code>0.1</code> seconds).</p>
</li>
<li>
<p>Click <strong>Deploy</strong> to apply changes.</p>
</li>
</ol>
<p>In this flow, we are configuring the production line based on the demo data, specifically for <strong>Line-1</strong>, as we are building the OEE dashboard for this line. The settings define the shift duration for the last <strong>X</strong> hours used in OEE calculations and the total shift duration within a <strong>24-hour</strong> period.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/configuration-flow-rcz5XK2913-928.avif 928w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/configuration-flow-rcz5XK2913-928.webp 928w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow to set basic configuration settings that will be used across the OEE dashboard flow for calculations." loading="lazy" decoding="async" src="https://flowfuse.com/img/configuration-flow-rcz5XK2913-928.jpeg" width="928" height="110" /></picture>
<em>Flow to set basic configuration settings that will be used across the OEE dashboard flow for calculations.</em></p>
<h4 id="retrieving-data-from-sqlite%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#retrieving-data-from-sqlite%3A"></a> Retrieving Data from SQLite:</h4>
<ol>
<li>
<p>Drag an <strong>Inject node</strong> and configure it to trigger at regular intervals.</p>
</li>
<li>
<p>Drag a <strong>Change node</strong> and add following elements:</p>
<ul>
<li>Set <code>msg.params</code> to <code>{}</code></li>
<li>Set <code>msg.params.$startTime</code> to <code>$moment($millis() - ($number($flowContext('shift_duration')) * 60 * 60 * 1000)).format('YYYY-MM-DD HH:mm:ss')</code></li>
<li>Set <code>msg.params.$endTime</code> to <code>$moment($millis()).format('YYYY-MM-DD HH:mm:ss')</code></li>
<li>Set <code>msg.params.$line</code> to <code>flow.line</code></li>
</ul>
</li>
<li>
<p>Drag an <strong>SQLite node</strong> and insert the following query:</p>
<div style="position: relative" id="code-container-205">
<pre class="language-sql"><code id="code-205" class="language-sql"><span class="token keyword">SELECT</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span> machine_name<span class="token punctuation">,</span> area<span class="token punctuation">,</span> line<span class="token punctuation">,</span> total_produced_units<span class="token punctuation">,</span> good_units<span class="token punctuation">,</span> defect_units<span class="token punctuation">,</span> target_output<br /><span class="token keyword">FROM</span> ProductionData<br /><span class="token keyword">WHERE</span> <span class="token keyword">timestamp</span> <span class="token operator">BETWEEN</span> $startTime <span class="token operator">AND</span> $endTime <span class="token operator">AND</span> line <span class="token operator">=</span> $line<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-205" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag a Change node onto the canvas and set the following element to store the retrived production data result as new property:</p>
<ul>
<li>Set <code>msg.payload</code> to <code>msg.productionData</code></li>
</ul>
</li>
<li>
<p>Connect the Inject node’s output to the input of the Change node that sets parameters. Then, connect the Change node’s output to the input of the SQLite node that retrieves production data. Finally, connect the SQLite node’s output to the input of last change node we added.</p>
</li>
<li>
<p>Drag another <strong>SQLite node</strong> and insert the following query:</p>
<div style="position: relative" id="code-container-228">
<pre class="language-sql"><code id="code-228" class="language-sql"><span class="token keyword">SELECT</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span> machine_name<span class="token punctuation">,</span> downtime_start<span class="token punctuation">,</span> downtime_duration_minutes<span class="token punctuation">,</span> downtime_reason<br /><span class="token keyword">FROM</span> DowntimeData<br /><span class="token keyword">WHERE</span> <span class="token keyword">timestamp</span> <span class="token operator">BETWEEN</span> $startTime <span class="token operator">AND</span> $endTime <span class="token operator">AND</span> line <span class="token operator">=</span> $line<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-228" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag a Change node onto the canvas and set the following element to store the retrived production data result as new property:</p>
<ul>
<li>Set <code>msg.payload</code> to <code>msg.downtimeData</code></li>
</ul>
</li>
<li>
<p>Connect the SQLite node’s output to the input of last change node we added.</p>
</li>
<li>
<p>Now, drag the Link Out node onto the canvas and connect it to the last Change node.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sqlite-flow-0e0rYCCLc9-1794.avif 1794w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sqlite-flow-0e0rYCCLc9-1794.webp 1794w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow that retrives the data from the sqlite table" loading="lazy" decoding="async" src="https://flowfuse.com/img/sqlite-flow-0e0rYCCLc9-1794.jpeg" width="1794" height="96" /></picture>
<em>Flow that retrives the data from the sqlite table</em></p>
<h3 id="preparing-data-for-oee-calculations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#preparing-data-for-oee-calculations"></a> Preparing Data for OEE Calculations</h3>
<p>Now that we have a flow to retrieve production and downtime data, we can calculate key OEE metrics. The total number of good units, defective units, total produced units, target output, and downtime duration are summed across all production lines. Using these values, we can calculate availability, performance, and quality for the entire production system, which can then be used to calculate OEE.</p>
<ol>
<li>
<p>Drag the <strong>link in node</strong> onto the canvas and connect it to the <strong>link out node</strong>.</p>
</li>
<li>
<p>Drag two <strong>Change nodes</strong> onto the canvas and connect them to the <strong>Link In</strong> node.</p>
<ul>
<li>In the first Change node, set <code>msg.payload</code> to <code>production_data</code>.</li>
<li>In the second Change node, set <code>msg.payload</code> to <code>downtime_data</code>.</li>
</ul>
</li>
<li>
<p>Drag a <strong>Split node</strong> onto the canvas and connect it to the first <strong>Change node</strong>, the one setting <code>production_data</code>. Configure the <strong>Split node</strong> so that <code>msg.payload</code> is assigned to <code>production_data</code>.</p>
</li>
<li>
<p>Drag three <strong>Join nodes</strong> onto the canvas and connect them to the <strong>Split node</strong> to sum individual data points. Configure each <strong>Join node</strong> with the following settings:</p>
<ul>
<li>Mode: Reduce Sequence</li>
<li>Initial Value: 0</li>
<li>Fix-up Expression: <code>$A</code></li>
</ul>
</li>
<li>
<p>Set the reduce expressions as follows:</p>
<ul>
<li>First Join Node: <code>$A + msg.payload.total_produced_units</code></li>
<li>Second Join Node: <code>$A + msg.payload.good_units</code></li>
<li>Third Join Node: <code>$A + msg.payload.target_output</code></li>
</ul>
</li>
<li>
<p>Drag three <strong>Change nodes</strong> onto the canvas and connect each to a <strong>Join node</strong>.</p>
</li>
<li>
<p>Configure these <strong>Change nodes</strong> to store the summed values using the following variables to flow context:</p>
<ul>
<li><code>total_produced_units</code></li>
<li><code>total_good_units</code></li>
<li><code>total_target_output</code></li>
</ul>
</li>
<li>
<p>Drag a <strong>Switch node</strong> onto the canvas and connect it to the <strong>Change node</strong> that sets the retrieved downtime data to <code>msg.payload</code>, for switch node set the Property to <code>msg.payload</code> and add the following conditions:</p>
<ul>
<li>is not empty.</li>
<li>Otherwise.</li>
</ul>
</li>
<li>
<p>Drag a <strong>Split node</strong> onto the canvas and connect it to the first output of the <strong>Switch node</strong>.</p>
</li>
<li>
<p>Drag a <strong>Join node</strong> onto the canvas and connect it to the <strong>Split node</strong>.</p>
</li>
<li>
<p>Configure the <strong>Join node</strong> with the following settings:</p>
<ul>
<li>Mode: Reduce Sequence</li>
<li>Initial Value: 0</li>
<li>Fix-up Expression: <code>$A</code></li>
<li>Reduce Expression: <code>$A + payload.downtime_duration_minutes</code></li>
</ul>
</li>
<li>
<p>Drag a <strong>Change node</strong> onto the canvas and connect it to the <strong>Join node</strong>, Configure this <strong>Change node</strong> to store the total downtime duration in the flow context with following element:</p>
<ul>
<li>Set <code>flow.total_downtime</code> to <code>msg.payload</code></li>
</ul>
</li>
<li>
<p>Drag another Change node onto the canvas and connect it to the second output of the Switch node, Set this Change node to store 0 in the flow context for total_downtime with following element:</p>
</li>
</ol>
<ul>
<li>Set <code>flow.total_downtime</code> to 0</li>
</ul>
<ol start="14">
<li>Drag a <strong>Link Out</strong> node onto the canvas and connect it to any of the Change nodes that store the summed metrics in the flow context.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/preparing-data-flow-7ktQmYkogX-1782.avif 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/preparing-data-flow-7ktQmYkogX-1782.webp 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow to prepare the data necessary to calculate OEE and all its three components." loading="lazy" decoding="async" src="https://flowfuse.com/img/preparing-data-flow-7ktQmYkogX-1782.jpeg" width="1782" height="344" /></picture>
<em>Flow to prepare the data necessary to calculate OEE and all its three components.</em></p>
<h3 id="calculating-oee-and-key-metrics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#calculating-oee-and-key-metrics"></a> Calculating OEE and Key Metrics</h3>
<p>Now that we have all the necessary pieces, we can calculate the key metrics for OEE: Availability, Performance, and Quality and later OEE.</p>
<ol>
<li>
<p>Drag a <strong>Link In node</strong>.</p>
</li>
<li>
<p>Drag a <strong>Change node</strong> and add element as following:</p>
<ul>
<li>Set <code>msg.quality</code> to <code>($flowContext('total_good_units') / $flowContext('total_produced_units')) * 100</code> as JSONata expression.</li>
<li>Set <code>msg.availability</code> to <code>(($flowContext('shift_duration') - $flowContext('total_downtime')) / $flowContext('shift_duration')) * 100</code> as JSONata expression.</li>
<li>Set <code>msg.performance</code> to <code>($flowContext('total_produced_units') / $flowContext('target_output')) * 100</code> as JSONata expression.</li>
<li>Set <code>msg.oee</code> to <code>$round(((msg.availability / 100) * (msg.performance / 100) * (msg.quality / 100)) * 100, 2)</code> as JSONata expression.</li>
<li>Set <code>msg.quality</code>to <code>$round(msg.quality, 2)</code></li>
<li>Set <code>msg.availability</code> to <code>$round(msg.availability, 2)</code></li>
<li>Set <code>msg.performance</code> to <code>$round(msg.performance, 2)</code></li>
<li>Set <code>msg.productionData</code> to JSONata expression:</li>
</ul>
<div style="position: relative" id="code-container-508">
<pre class="language-json"><code id="code-508" class="language-json"><span class="token punctuation">[</span><br /><span class="token punctuation">{</span><br /> <span class="token property">"reason"</span><span class="token operator">:</span> <span class="token string">"Total Good Units Produced"</span><span class="token punctuation">,</span><br /> <span class="token property">"units"</span><span class="token operator">:</span> $flowContext(<span class="token string">"total_good_units"</span>)<br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"series"</span><span class="token operator">:</span> <span class="token string">"Total Defective Units Produced"</span><span class="token punctuation">,</span><br /> <span class="token property">"units"</span><span class="token operator">:</span> $number($flowContext(<span class="token string">"total_produced_units"</span>)) - $number($flowContext(<span class="token string">"total_good_units"</span>))<br /> <span class="token punctuation">}</span><br /><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-508" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag a <strong>Link Out node</strong> and connect it to the <strong>Change node</strong>.</p>
</li>
<li>
<p>Drag a separate <strong>Link In</strong> node for visualization and keep it in a separate flow. This will be the <strong>Link In</strong> node where all the calculated final data for visualization will be stored.</p>
</li>
<li>
<p>Connect <strong>link out</strong> node to this <strong>link in</strong> node.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/calculate-oee-and-key-metrics-nYJxmSrcAP-1112.avif 1112w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/calculate-oee-and-key-metrics-nYJxmSrcAP-1112.webp 1112w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow that calculates availability, quality, performance, and OEE, and also prepares production data for visualization." loading="lazy" decoding="async" src="https://flowfuse.com/img/calculate-oee-and-key-metrics-nYJxmSrcAP-1112.jpeg" width="1112" height="106" /></picture>
<em>Flow that calculates availability, quality, performance, and OEE, and also prepares production data for visualization.</em></p>
<h3 id="detailed-breakdown-of-oee-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#detailed-breakdown-of-oee-data"></a> Detailed Breakdown of OEE Data</h3>
<p>We have calculated the OEE and other key metrics. However, as discussed in the planning section of our previous article, we will also visualize recent downtime events, a downtime summary, the top underperforming machines (OEE-wise), and the OEE trend over the last 30 days on the dashboard.</p>
<p>Let’s do that.</p>
<ol>
<li>
<p>Drag the <strong>link in node</strong> onto the canvas and connect it to the <strong>link out node</strong> that is part of the SQLite flow, which is also connected to the change node that sets the retrieved downtime result to <code>msg.payload</code>.</p>
</li>
<li>
<p>Drag a <strong>change node</strong> onto the canvas and set the following element: <code>Set msg.downtime_data to msg.payload</code>.</p>
</li>
</ol>
<h4 id="downtime-summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#downtime-summary"></a> Downtime Summary</h4>
<ol>
<li>Drag a <strong>function node</strong> onto the canvas and add the following JavaScript to calculate the downtime summary:</li>
</ol>
<div style="position: relative" id="code-container-560">
<pre class="language-javascript"><code id="code-560" class="language-javascript"><span class="token keyword">function</span> <span class="token function">calculateDowntimeByReason</span><span class="token punctuation">(</span><span class="token parameter">downtimeData</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>downtimeData<span class="token punctuation">)</span> <span class="token operator">||</span> downtimeData<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> f<br /> <span class="token punctuation">}</span><br /> <span class="token keyword">const</span> summary <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br /> downtimeData<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> downtime_reason<span class="token punctuation">,</span> downtime_duration_minutes <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> summary<span class="token punctuation">[</span>downtime_reason<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>summary<span class="token punctuation">[</span>downtime_reason<span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">+</span> downtime_duration_minutes<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span>summary<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">[</span>reason<span class="token punctuation">,</span> duration<span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token literal-property property">downtime_reason</span><span class="token operator">:</span> reason<span class="token punctuation">,</span><br /> <span class="token literal-property property">downtime_duration_minutes</span><span class="token operator">:</span> duration<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token function">calculateDowntimeByReason</span><span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-560" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>
<p>Drag a <strong>change node</strong> onto the canvas and set:</p>
<ul>
<li>Set <code>msg.payload</code> to <code>msg.downtimeSummary</code>.</li>
</ul>
</li>
<li>
<p>Drag a <strong>link out</strong> node and connect it to the change node that sets <code>msg.downtime_data</code> to <code>msg.payload</code>.</p>
</li>
<li>
<p>Connect this <strong>link out node</strong> to the <strong>link in node</strong> that we added earlier to receive all the calculated metrics for visualization.</p>
</li>
</ol>
<h4 id="recent-downtime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#recent-downtime"></a> Recent Downtime</h4>
<ol>
<li>
<p>Drag a <strong>Switch</strong> node onto the canvas and set the property to <strong>msg.payload</strong>. Add the following condition:</p>
<ul>
<li><strong>is not empty</strong></li>
<li><strong>otherwise</strong></li>
</ul>
</li>
<li>
<p>Drag a <strong>Split</strong> node onto the canvas and connect it to the <strong>first output</strong> of the <strong>Switch</strong> node.</p>
</li>
<li>
<p>Drag a <strong>Sort</strong> node onto the canvas and connect it to the <strong>Split</strong> node. Set the sort to <strong>"message sequence"</strong>, key to <strong><code>msg.payload.downtime_start</code></strong>, and order to <strong>"descending."</strong> This will sort the downtime data from <strong>most recent to oldest</strong> based on its start time.</p>
</li>
<li>
<p>Drag a <strong>Join</strong> node onto the canvas and set the mode to <strong>automatic</strong>, then connect it to the <strong>Sort</strong> node.</p>
</li>
<li>
<p>Drag a <strong>Change</strong> node onto the canvas and set the following element:</p>
<ul>
<li><strong>Set <code>msg.recentDowntime</code> to <code>payload^(10)</code> as a JSONata expression.</strong></li>
</ul>
</li>
<li>
<p>Connect the <strong>Change</strong> node to the <strong>Link Out</strong> node that was added before.</p>
</li>
</ol>
<h4 id="top-underperforming-machines" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#top-underperforming-machines"></a> Top Underperforming Machines</h4>
<ol>
<li>
<p>Drag a <strong>Function</strong> node onto the canvas and connect it to the <strong>Link In</strong> node that is receiving the <strong>SQLite result</strong>.</p>
</li>
<li>
<p>Add the following <strong>JavaScript</strong> code to the <strong>Function</strong> node:</p>
</li>
</ol>
<div style="position: relative" id="code-container-654">
<pre class="language-javascript"><code id="code-654" class="language-javascript"><span class="token keyword">const</span> productionData <span class="token operator">=</span> msg<span class="token punctuation">.</span>production_data<span class="token punctuation">;</span><br /><span class="token keyword">const</span> downtimeEvents <span class="token operator">=</span> msg<span class="token punctuation">.</span>downtime_data<span class="token punctuation">;</span><br /><span class="token keyword">const</span> shiftDuration <span class="token operator">=</span> <span class="token punctuation">(</span>flow<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'shift_duration'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">;</span> <span class="token comment">// Convert hours to minutes</span><br /><br /><span class="token comment">// Group production data by machine (including area)</span><br /><span class="token keyword">let</span> machineData <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br />productionData<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">data</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">total_produced_units</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">good_units</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">target_output</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">area</span><span class="token operator">:</span> data<span class="token punctuation">.</span>area <span class="token comment">// Store area</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span><span class="token punctuation">.</span>total_produced_units <span class="token operator">+=</span> data<span class="token punctuation">.</span>total_produced_units<span class="token punctuation">;</span><br /> machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span><span class="token punctuation">.</span>good_units <span class="token operator">+=</span> data<span class="token punctuation">.</span>good_units<span class="token punctuation">;</span><br /> machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span><span class="token punctuation">.</span>target_output <span class="token operator">+=</span> data<span class="token punctuation">.</span>target_output<span class="token punctuation">;</span><br /> machineData<span class="token punctuation">[</span>data<span class="token punctuation">.</span>machine_name<span class="token punctuation">]</span><span class="token punctuation">.</span>count <span class="token operator">+=</span> <span class="token number">1</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> oeeResults <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>machineData<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">machineName</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> data <span class="token operator">=</span> machineData<span class="token punctuation">[</span>machineName<span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">let</span> machineDowntime <span class="token operator">=</span> downtimeEvents<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">event</span> <span class="token operator">=></span> event<span class="token punctuation">.</span>machine_name <span class="token operator">===</span> machineName<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">function</span> <span class="token function">calculateOEE</span><span class="token punctuation">(</span><span class="token parameter">data<span class="token punctuation">,</span> downtime</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>target_output <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">availability</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token literal-property property">performance</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token literal-property property">quality</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token literal-property property">oee</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">let</span> totalDowntime <span class="token operator">=</span> downtime<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> event</span><span class="token punctuation">)</span> <span class="token operator">=></span><br /> <span class="token keyword">typeof</span> event<span class="token punctuation">.</span>downtime_duration_minutes <span class="token operator">===</span> <span class="token string">'number'</span> <span class="token operator">?</span> acc <span class="token operator">+</span> event<span class="token punctuation">.</span>downtime_duration_minutes <span class="token operator">:</span> acc<br /> <span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">let</span> availability <span class="token operator">=</span> <span class="token punctuation">(</span>shiftDuration <span class="token operator">-</span> totalDowntime<span class="token punctuation">)</span> <span class="token operator">/</span> shiftDuration<span class="token punctuation">;</span><br /> availability <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> availability<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">let</span> performance <span class="token operator">=</span> data<span class="token punctuation">.</span>target_output <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> data<span class="token punctuation">.</span>total_produced_units <span class="token operator">/</span> data<span class="token punctuation">.</span>target_output <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span><br /> <span class="token keyword">let</span> quality <span class="token operator">=</span> data<span class="token punctuation">.</span>total_produced_units <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> data<span class="token punctuation">.</span>good_units <span class="token operator">/</span> data<span class="token punctuation">.</span>total_produced_units <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">let</span> oee <span class="token operator">=</span> availability <span class="token operator">*</span> performance <span class="token operator">*</span> quality<span class="token punctuation">;</span><br /><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">availability</span><span class="token operator">:</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span><span class="token punctuation">(</span>availability <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">performance</span><span class="token operator">:</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span><span class="token punctuation">(</span>performance <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">quality</span><span class="token operator">:</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span><span class="token punctuation">(</span>quality <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">oee</span><span class="token operator">:</span> <span class="token function">parseFloat</span><span class="token punctuation">(</span><span class="token punctuation">(</span>oee <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token keyword">let</span> metrics <span class="token operator">=</span> <span class="token function">calculateOEE</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> machineDowntime<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">machine_name</span><span class="token operator">:</span> machineName<span class="token punctuation">,</span><br /> <span class="token literal-property property">area</span><span class="token operator">:</span> data<span class="token punctuation">.</span>area<span class="token punctuation">,</span><br /> <span class="token literal-property property">oee</span><span class="token operator">:</span> metrics<span class="token punctuation">.</span>oee<br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Filter only machines with OEE < 85</span><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> oeeResults<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">machine</span> <span class="token operator">=></span> machine<span class="token punctuation">.</span>oee <span class="token operator"><</span> <span class="token number">85</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-654" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/downtime-events-summery-oee-machine-wise-aiC2WDxc4t-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/downtime-events-summery-oee-machine-wise-aiC2WDxc4t-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flow that prepares the Recent Downtime, Downtime Summary, and Top Underperforming Machines (OEE-wise)
" loading="lazy" decoding="async" src="https://flowfuse.com/img/downtime-events-summery-oee-machine-wise-aiC2WDxc4t-1920.jpeg" width="1920" height="273" /></picture>
<em>Flow that prepares the Recent Downtime, Downtime Summary, and Top Underperforming Machines (OEE-wise)</em></p>
<h4 id="oee-trend-for-the-last-30-days" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#oee-trend-for-the-last-30-days"></a> OEE Trend for the Last 30 Days</h4>
<p>Now, to calculate the OEE for the last 30 days, we need the complete production and downtime data for that period. However, the current SQLite flow retrieves only the last 12 hours. Therefore, we need another SQLite flow to retrieve data from the last 30 days.</p>
<h5 id="retrieving-production-and-downtime-data-for-the-last-30-days" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#retrieving-production-and-downtime-data-for-the-last-30-days"></a> Retrieving Production and Downtime Data for the Last 30 Days</h5>
<ol>
<li>
<p>Copy the existing <strong>SQLite flow</strong> from the <strong>Inject node</strong> to the <strong>Change node</strong> that sets the retrieved downtime result to <code>msg.payload</code>.</p>
</li>
<li>
<p>Click on the <strong>Change node</strong> that sets the parameters for the SQL query, keep only the element setting the line parameter, and remove the rest.</p>
</li>
<li>
<p>Modify the first SQLite node's SQL query to the following:</p>
</li>
</ol>
<div style="position: relative" id="code-container-684">
<pre class="language-sql"><code id="code-684" class="language-sql"><span class="token keyword">SELECT</span><br /> <span class="token keyword">timestamp</span> <span class="token keyword">AS</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span><br /> machine_name <span class="token keyword">AS</span> machine_name<span class="token punctuation">,</span><br /> area <span class="token keyword">AS</span> area<span class="token punctuation">,</span><br /> line <span class="token keyword">AS</span> line<span class="token punctuation">,</span><br /> total_produced_units <span class="token keyword">AS</span> total_produced_units<span class="token punctuation">,</span><br /> good_units <span class="token keyword">AS</span> good_units<span class="token punctuation">,</span><br /> defect_units <span class="token keyword">AS</span> defect_units<span class="token punctuation">,</span><br /> target_output <span class="token keyword">AS</span> target_output<br /><span class="token keyword">FROM</span> ProductionData<br /><span class="token keyword">WHERE</span> line <span class="token operator">=</span> $line<br /> <span class="token operator">AND</span> <span class="token keyword">timestamp</span> <span class="token operator">>=</span> <span class="token keyword">datetime</span><span class="token punctuation">(</span><span class="token string">'now'</span><span class="token punctuation">,</span> <span class="token string">'-30 days'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-684" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Modify the second SQLite node's SQL query to the following:</li>
</ol>
<div style="position: relative" id="code-container-692">
<pre class="language-sql"><code id="code-692" class="language-sql"><span class="token keyword">SELECT</span><br /> <span class="token keyword">timestamp</span> <span class="token keyword">AS</span> <span class="token keyword">timestamp</span><span class="token punctuation">,</span><br /> machine_name <span class="token keyword">AS</span> machine_name<span class="token punctuation">,</span><br /> downtime_start <span class="token keyword">AS</span> downtime_start<span class="token punctuation">,</span><br /> downtime_duration_minutes <span class="token keyword">AS</span> downtime_duration_minutes<span class="token punctuation">,</span><br /> downtime_reason <span class="token keyword">AS</span> downtime_reason<br /><span class="token keyword">FROM</span> DowntimeData<br /><span class="token keyword">WHERE</span><br /> <span class="token keyword">timestamp</span> <span class="token operator">BETWEEN</span> $startTime <span class="token operator">AND</span> $endTime<br /> <span class="token operator">AND</span> line <span class="token operator">=</span> $line<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-692" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h5 id="calculating-last-30d-days-oee" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#calculating-last-30d-days-oee"></a> Calculating last 30d days OEE</h5>
<ol>
<li>
<p>Drag the <strong>Link Out node</strong> onto the canvas and connect it to the last <strong>Change node</strong> of the SQLite flow.</p>
</li>
<li>
<p>Drag the <strong>Link In node</strong> onto the canvas and connect it to the last <strong>Link Out node</strong>.</p>
</li>
<li>
<p>Drag the <strong>Function node</strong> onto the canvas, add the following JavaScript, and connect the Function node to the <strong>Link In node</strong>:</p>
</li>
</ol>
<div style="position: relative" id="code-container-713">
<pre class="language-javascript"><code id="code-713" class="language-javascript"><span class="token keyword">let</span> productionData <span class="token operator">=</span> msg<span class="token punctuation">.</span>production_data<span class="token punctuation">;</span><br /><span class="token keyword">let</span> downtimeData <span class="token operator">=</span> msg<span class="token punctuation">.</span>downtime_data<span class="token punctuation">;</span><br /><span class="token keyword">let</span> line <span class="token operator">=</span> flow<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'line'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token keyword">let</span> shiftDuration <span class="token operator">=</span> flow<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'shiftDuration24h'</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">3600</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> groupedData <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span><br /><br />productionData<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">entry</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>entry<span class="token punctuation">.</span>line <span class="token operator">===</span> line<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> date <span class="token operator">=</span> entry<span class="token punctuation">.</span>timestamp<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">totalShiftDuration</span><span class="token operator">:</span> shiftDuration<span class="token punctuation">,</span><br /> <span class="token literal-property property">totalGoodUnits</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">totalProducedUnits</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">totalDowntimeSeconds</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">totalCycleTime</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">cycleCount</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">totalTargetOutput</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">timestamp</span><span class="token operator">:</span> entry<span class="token punctuation">.</span>timestamp<br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>totalGoodUnits <span class="token operator">+=</span> entry<span class="token punctuation">.</span>good_units<span class="token punctuation">;</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>totalProducedUnits <span class="token operator">+=</span> entry<span class="token punctuation">.</span>total_produced_units<span class="token punctuation">;</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>totalCycleTime <span class="token operator">+=</span> entry<span class="token punctuation">.</span>cycle_time<span class="token punctuation">;</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>cycleCount<span class="token operator">++</span><span class="token punctuation">;</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>totalTargetOutput <span class="token operator">+=</span> entry<span class="token punctuation">.</span>target_output<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br />downtimeData<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">downtime</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>downtime<span class="token punctuation">.</span>line <span class="token operator">===</span> line<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> date <span class="token operator">=</span> downtime<span class="token punctuation">.</span>timestamp<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">" "</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> groupedData<span class="token punctuation">[</span>date<span class="token punctuation">]</span><span class="token punctuation">.</span>totalDowntimeSeconds <span class="token operator">+=</span> downtime<span class="token punctuation">.</span>downtime_duration_minutes <span class="token operator">*</span> <span class="token number">60</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">let</span> oeeResults <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span>groupedData<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">[</span>date<span class="token punctuation">,</span> data<span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> avgCycleTime <span class="token operator">=</span> data<span class="token punctuation">.</span>cycleCount <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> data<span class="token punctuation">.</span>totalCycleTime <span class="token operator">/</span> data<span class="token punctuation">.</span>cycleCount <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span><br /> <span class="token keyword">let</span> availableTime <span class="token operator">=</span> data<span class="token punctuation">.</span>totalShiftDuration <span class="token operator">-</span> data<span class="token punctuation">.</span>totalDowntimeSeconds<span class="token punctuation">;</span><br /> <span class="token keyword">let</span> availability <span class="token operator">=</span> availableTime <span class="token operator">/</span> data<span class="token punctuation">.</span>totalShiftDuration<span class="token punctuation">;</span><br /> <span class="token keyword">let</span> performance <span class="token operator">=</span> data<span class="token punctuation">.</span>totalTargetOutput <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> data<span class="token punctuation">.</span>totalProducedUnits <span class="token operator">/</span> data<span class="token punctuation">.</span>totalTargetOutput <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span><br /> <span class="token keyword">let</span> quality <span class="token operator">=</span> data<span class="token punctuation">.</span>totalProducedUnits <span class="token operator">></span> <span class="token number">0</span> <span class="token operator">?</span> data<span class="token punctuation">.</span>totalGoodUnits <span class="token operator">/</span> data<span class="token punctuation">.</span>totalProducedUnits <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">;</span><br /> <span class="token keyword">let</span> oee <span class="token operator">=</span> <span class="token punctuation">(</span>availability <span class="token operator">*</span> performance <span class="token operator">*</span> quality <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toFixed</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span> date<span class="token punctuation">,</span> availability<span class="token punctuation">,</span> performance<span class="token punctuation">,</span> quality<span class="token punctuation">,</span> oee<span class="token punctuation">,</span> <span class="token literal-property property">timestamp</span><span class="token operator">:</span> data<span class="token punctuation">.</span>timestamp <span class="token punctuation">}</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Sort data by timestamp (oldest to most recent)</span><br />oeeResults<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>b<span class="token punctuation">.</span>timestamp<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> oeeResults<span class="token punctuation">;</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-713" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>
<p>Drag the <strong>Change node</strong> onto the canvas and set <code>msg.payload</code> to <code>msg.oeeTrend</code>.</p>
</li>
<li>
<p>Drag the <strong>Link Out node</strong> onto the canvas and connect it to the <strong>Change node</strong>.</p>
</li>
<li>
<p>Connect this <strong>Link Out node</strong> to the <strong>Link In node</strong> that was added earlier to receive all the calculated metrics for visualization.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-trend-bOQcpg_F0I-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-trend-bOQcpg_F0I-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Flows that calculate the OEE for each day over the last 30 days" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-trend-bOQcpg_F0I-1920.jpeg" width="1920" height="230" /></picture>
<em>Flows that calculate the OEE for each day over the last 30 days.</em></p>
<h3 id="building-the-oee-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#building-the-oee-dashboard"></a> Building the OEE Dashboard</h3>
<p>Now that the key OEE metrics have been calculated and detailed insights into production performance have been gathered, it is time to bring everything together in a visually intuitive and interactive dashboard. The OEE dashboard will provide real-time visibility into availability, performance, and quality while also displaying recent downtime events, downtime summaries, underperforming machines, and historical OEE trends.</p>
<p>Using FlowFuse Dashboard (Node-RED Dashboard 2.0), a clean and efficient interface will be designed, allowing operators and decision-makers to monitor production efficiency at a glance.</p>
<ol>
<li>
<p>Drag a Switch node onto the canvas, set the property to <code>msg.oee</code>, and add the condition:</p>
<ul>
<li>"Is not null".</li>
</ul>
</li>
<li>
<p>Connect it to the Link-In node that receives calculated metrics.</p>
</li>
<li>
<p>Drag a Change node, set <code>msg.oee</code> to <code>msg.payload</code>, and connect it to a Gauge widget.</p>
<ul>
<li>Create a new Group on a new page named <strong>Line-1</strong>.</li>
<li>Set the page layout to <strong>Grid</strong>, adjust the range from <strong>0 to 100</strong>, and label the gauge <strong>OEE</strong>.</li>
<li>Choose <strong>Half Gauge</strong> as the type, set the style to <strong>Rounded</strong>, and adjust the width and height to <strong>6</strong> and <strong>3</strong> for both the group and the widget.</li>
</ul>
</li>
<li>
<p>Repeat these steps for <code>msg.quality</code>, <code>msg.availability</code>, and <code>msg.performance</code>, ensuring each has a separate Group with the correct label.</p>
</li>
<li>
<p>Drag a Switch node for <code>msg.productionData</code> and connect it to a Change node setting <code>msg.productionData</code> to <code>msg.payload</code>:</p>
<ul>
<li>"Is not null".</li>
</ul>
</li>
<li>
<p>Repeat this step for <code>msg.downtimeSummary</code>, <code>msg.recentDowntime</code>, <code>msg.topUnderPerformingMachines</code>, and <code>msg.oeeTrend</code>, ensuring each has a separate Switch node and Change node.</p>
</li>
<li>
<p>Drag a Bar Chart widget, create a new Group, set the width to <strong>6</strong> and height to <strong>8</strong> for both the group and widget, label it <strong>Production Data</strong>, group data by <strong>Stacks</strong>, and map <strong>X to series</strong> and <strong>Y to units</strong>. Connect it to the node setting <code>msg.productionData</code>.</p>
</li>
<li>
<p>Duplicate the chart for <strong>Downtime Summary</strong>, mapping <strong>X to downtime_reason</strong> and <strong>Y to downtime_duration_minutes</strong>, and connect it to the node setting <code>msg.downtimeSummary</code> to <code>msg.payload</code>.</p>
</li>
<li>
<p>Drag a Table widget, create a new Group, set width <strong>6</strong> and height <strong>2</strong> for both the group and widget, label it <strong>Recent Downtime Events</strong>, set max rows to <strong>5</strong>, and add columns with keys:</p>
<ul>
<li><code>machine_name</code></li>
<li><code>downtime_start</code></li>
<li><code>downtime_duration_minutes</code></li>
<li><code>downtime_reason</code></li>
</ul>
</li>
<li>
<p>Connect it to the node setting <code>msg.recent_downtime</code> to <code>msg.payload</code>.</p>
</li>
<li>
<p>Duplicate the table for <strong>Top Underperforming Machines</strong>, adding columns with keys:</p>
<ul>
<li><code>machine_name</code></li>
<li><code>area</code></li>
<li><code>oee</code></li>
</ul>
</li>
<li>
<p>Connect it to <code>msg.topUnderPerformingMachines</code>.</p>
</li>
<li>
<p>Drag a Line Chart widget, create a new Group, set width <strong>12</strong> and height <strong>5</strong> for both the group and widget, label it <strong>Daily OEE Trend Over 24 Hours</strong>, set X-axis to <strong>Timescale</strong>, format <strong>Y-l-d</strong>, and map <strong>X to <code>date</code></strong> and <strong>Y to <code>oee</code></strong>. Connect it to the Change node setting <code>msg.oeeTrend</code> to <code>msg.payload</code>.</p>
</li>
<li>
<p>Click <strong>Deploy</strong>.</p>
</li>
<li>
<p>Open the dashboard by clicking the Dashboard 2.0 button located at the top-right corner of the Dashboard 2.0 sidebar.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-DnpT8DVaN4-1744.avif 1744w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-DnpT8DVaN4-1744.webp 1744w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard UI flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-DnpT8DVaN4-1744.jpeg" width="1744" height="510" /></picture>
<em>OEE Dashboard UI flow</em></p>
<p>Your OEE dashboard is now set up and ready to use. It will visualize key metrics, including OEE, quality, availability, performance, production data, downtime events, and machine performance trends.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-without-style-97pFe0dhov-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-without-style-97pFe0dhov-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard results without proper theming and styling" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-without-style-97pFe0dhov-1920.jpeg" width="1920" height="1090" /></picture>
<em>OEE Dashboard results without proper theming and styling.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-without-style-2-aH-1pCgeT2-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-without-style-2-aH-1pCgeT2-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard results without proper theming and styling" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-without-style-2-aH-1pCgeT2-1920.jpeg" width="1920" height="1086" /></picture>
<em>OEE Dashboard results without proper theming and styling</em></p>
<p>However, the dashboard may not yet look exactly as it did in the previous design or intended layout. Some components may not align correctly with adjacent components in terms of width and height. Additionally, on different screens, you may notice layout inconsistencies, and the top header elements, such as the OEE Dashboard title and logo is missing.</p>
<p>Do not worry—in the next part of this series, we will style the dashboard to match the original design. Later, we will demonstrate how to connect it to a real data source, scale it across your production lines, and explain how you can use this dashboard to improve production efficiency.</p>
<h2 id="what-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/#what-next%3F"></a> What Next?</h2>
<p>Part 3 of this series will follow soon. In the meantime, if you’re excited to quickly launch your OEE dashboard in your factory environment, don’t delay! <a href="https://app.flowfuse.com/account/create">Register for a FlowFuse account</a> now and initiate your journey with our new effective, ready-made <a href="https://flowfuse.com/blueprints/manufacturing/oee-dashboard/">OEE Dashboard Blueprint</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/Part 1: Building an OEE Dashboard with FlowFuseDefining OEE and Planning an Effective Dashboard2025-04-01T00:00:00ZSumit Shinde<p>OEE (Overall Equipment Effectiveness) is a KPI used in manufacturing to measure equipment performance based on availability, efficiency, and quality.</p>
<p>To effectively track this KPI, an OEE dashboard is built, but creating one can be complex, especially when consolidating data from various sources, with limited flexibility to integrate data across different systems. Additionally, building a customizable dashboard to suit specific needs adds another layer of complexity.</p>
<!--more-->
<p>With FlowFuse, it's possible to build a customized, OEE Dashboard, without writing any code, that can provide real-time production data based on your needs.</p>
<p>In this first part of a new blog series on building an OEE dashboard with FlowFuse, we explain the concept of OEE, how it is calculated, and outline the basic plan for the dashboard. In that plan, we cover the scope of OEE calculation, key metrics, visualization strategies, and the expected design of the dashboard.</p>
<p>Let’s get started!</p>
<h2 id="what-is-oee%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#what-is-oee%3F"></a> What is OEE?</h2>
<p>Overall Equipment Effectiveness (OEE) is a crucial metric in manufacturing that assesses the productivity of equipment through three key components. These components evaluate the efficiency of equipment during the production process:</p>
<ul>
<li><strong>Availability:</strong> How often does the equipment perform when needed?</li>
<li><strong>Performance:</strong> How much product does the equipment produce?</li>
<li><strong>Production Quality:</strong> How many high-quality products does the equipment produce?</li>
</ul>
<p>The concept of OEE was introduced by Seiichi Nakajima in the 1960s as part of the <a href="https://en.wikipedia.org/wiki/Total_productive_maintenance">Total Productive Maintenance (TPM)</a> initiative in Japan. Nakajima, an engineer at the Japan Institute of Plant Maintenance (JIPM), developed OEE to measure and enhance manufacturing productivity by identifying inefficiencies. This metric has since become widely adopted across the manufacturing industry. Today, OEE remains one of the most critical KPIs, with a really huge number of manufacturers considering it either important or very important for improving production efficiency and minimizing waste.</p>
<p>Measuring and improving OEE allows you to improve the utilization of existing machinery and improves operational efficiency. In many cases, improving OEE is the most strategic and cost-effective approach to increasing output.</p>
<h2 id="how-is-oee-calculated%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#how-is-oee-calculated%3F"></a> How is OEE calculated?</h2>
<p>OEE is calculated using the formula:</p>
<p><em><strong>OEE (%) = Availability × Performance × Quality</strong></em></p>
<p>Where:</p>
<ul>
<li><em><strong>Availability (%) = (Operating Time ÷ Planned Production Time) × 100</strong></em></li>
<li><em><strong>Performance (%) = (Actual Output ÷ Maximum Possible Output) × 100</strong></em></li>
<li><em><strong>Quality (%) = (Good Products ÷ Total Products) × 100</strong></em></li>
</ul>
<p>For example, if a machine is available 90% of the time, runs at 95% of its ideal speed, and 98% of products are defect-free, your OEE would be: 0.90 × 0.95 × 0.98 = 83.7%</p>
<h2 id="planning-your-oee-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#planning-your-oee-dashboard"></a> Planning Your OEE Dashboard</h2>
<p>Now that we’ve covered what OEE is, let's focus on designing a basic plan that details what are the things that we should display on our dashboard. This should consist of three parts:</p>
<ul>
<li><strong>Scope of the Calculation:</strong> How much data will be collected and analyzed?</li>
<li><strong>Key Metrics:</strong> Which metrics are the most important to track?</li>
<li><strong>Layout & Visualisation:</strong> What visual elements will be used to present the data?</li>
</ul>
<h3 id="defining-the-scope-of-oee-calculation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#defining-the-scope-of-oee-calculation"></a> Defining the Scope of OEE Calculation</h3>
<p>The first and most important step before creating the dashboard is defining the scope of the OEE calculation. The tracking level can vary based on the focus on area. Scope can vary between:</p>
<ul>
<li><strong>Machine-level OEE:</strong> Concentrates on individual machines, aiding in the identification of specific inefficiencies that impact performance.</li>
<li><strong>Line-level OEE:</strong> Assesses the entire production line, offering insights into the collaboration of multiple machines and pinpointing where bottlenecks arise.</li>
<li><strong>Factory-level OEE:</strong> Compiles data from various production lines to provide a comprehensive overview of overall efficiency and trends.</li>
</ul>
<p>For those building dashboards from scratch, it’s advisable to start at the machine level. This approach allows for faster time to value, as data collection can typically begin from a single point, reducing initial complexity. Once you’ve established the machine-level tracking and identified the inefficiencies, you can scale up to line-level and eventually factory-level OEE. Starting with machine-level data ensures that you can quickly uncover key insights and iteratively improve the scope and detail of your dashboard.</p>
<p>For this series, we will be building the dashboard at the line-level. In this case, we will collect data specific to a production line and perform the OEE calculation based on that data.</p>
<h3 id="key-metrics-and-insights" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#key-metrics-and-insights"></a> Key Metrics and Insights</h3>
<p>As mentioned earlier, the dashboard will calculate OEE for a production line, presenting key metrics such as availability, performance, quality, and the overall OEE score. While the overall OEE score provides a quick snapshot of performance, it does not offer enough detail to pinpoint specific areas that need improvement.</p>
<p>To address this, the dashboard will break down the OEE calculation at the machine level as well, enabling managers to identify underperforming machines that affect overall efficiency. Additionally, it will display recent downtime incidents, summarizing this data to uncover trends and identify potential root causes. This breakdown will provide a clearer understanding of where inefficiencies are occurring and allow for targeted corrective actions.</p>
<p>The dashboard will also track production quality, displaying the number of acceptable versus defective parts to ensure a continued focus on quality control. Additionally, last 30-days OEE trend analysis will be included, offering insights into performance changes over time. This will help managers identify patterns, monitor improvements, and highlight areas requiring attention.</p>
<h3 id="dashboard-visualization-%26-ui-design" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#dashboard-visualization-%26-ui-design"></a> Dashboard Visualization & UI Design</h3>
<p>To ensure that insights are easy to understand and act upon, the dashboard will feature a well-structured visual layout that presents complex data in a clear and intuitive manner. After analyzing various OEE dashboards, I designed this one with a focus on clarity, usability, and actionable insights. It will include gauges for a quick OEE overview, bar charts to track downtime and production trends, tables to highlight underperforming machines and recent downtime events, and line charts to monitor efficiency patterns over time. This setup ensures managers can quickly spot problems, understand their causes, and take the necessary steps to optimize production.</p>
<p>The following dashboard image illustrates the intended design and key objectives of our OEE dashboard. Based on the plan outlined in this part, we will build the dashboard interface in the next part of the series using simulated production and downtime data.</p>
<p>Later, we will show how to connect real factory data, scale the dashboard across multiple production lines, and use it to enhance OEE effectively.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-1-I5LCHq52dq-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-1-I5LCHq52dq-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-1-I5LCHq52dq-1920.jpeg" width="1920" height="1001" /></picture>
<em>OEE Dashboard</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/oee-dashboard-2--3q43dcXrH-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/oee-dashboard-2--3q43dcXrH-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="OEE Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/oee-dashboard-2--3q43dcXrH-1920.jpeg" width="1920" height="1001" /></picture>
<em>OEE Dashboard</em></p>
<h2 id="what-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-part-1/#what-next"></a> What Next</h2>
<p>Here’s <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/04/building-oee-dashboard-with-flowfuse-2/">Part 2</a> on the next steps to build your OEE dashboard with FlowFuse. But if you rather skip the tutorials and dive straight in, just <a href="https://app.flowfuse.com/account/create">register for a FlowFuse account</a> and start using our ready-made <a href="https://flowfuse.com/blueprints/manufacturing/oee-dashboard/">OEE Dashboard Blueprint</a> to optimize your operations and boost efficiency right away!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/Managing MQTT Connections at Scale in FlowFuseAutomating MQTT Configuration in FlowFuse Using Environment Variables2025-03-28T00:00:00ZSumit Shinde<p>FlowFuse makes it easy to deploy Node-RED flows at scale using DevOps pipelines and device groups. However, different stages in a pipeline may need different MQTT brokers—for example, one for development and another for production. Manually configuring each stage can be time-consuming, especially when a stage has multiple remote instances (devices).</p>
<!--more-->
<p>This article shows how to continue using FlowFuse’s one-click deployment while ensuring that remote instances in each stage of your pipeline connect to the desired MQTT broker without manual configuration.</p>
<h2 id="goal-and-prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#goal-and-prerequisites"></a> Goal and Prerequisites</h2>
<p>This article explains how to deploy Node-RED flows across different pipeline stages while ensuring each stage connects to its appropriate MQTT broker.</p>
<p>To proceed, ensure that the DevOps pipeline is created with the correct stages. If a stage includes remote instances, verify that all instances are running the FlowFuse Device Agent and are connected to your team.</p>
<p>For more information on how to create a DevOps pipeline, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/">How to Build and Automate DevOps Pipelines for Node-RED Deployments</a>. For instructions on how to create a device group, refer to the <a href="https://flowfuse.com/docs/user/device-groups/">Device Groups Documentation</a>.</p>
<h2 id="setting-environment-variables-for-development-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#setting-environment-variables-for-development-instance"></a> Setting Environment Variables for Development Instance</h2>
<p>For this guide, environment variables will be the key tool to ensure each pipeline stage connects to the correct MQTT broker without manual intervention, allowing for a smooth deployment process.</p>
<p>Since the development remote instance is where the flow will be built and tested, start by adding the necessary environment variables for its MQTT configuration. Setting these up first ensures the flow runs as expected before deploying it to other pipeline stages.</p>
<ol>
<li>
<p>Go to the remote/hosted instance settings in the FlowFuse platform.</p>
</li>
<li>
<p>Switch to the <strong>Environment</strong> settings.</p>
</li>
<li>
<p>Add the following environment variables with the appropriate values for that specific device:</p>
<ul>
<li><code>HOST</code> – The MQTT broker's hostname or IP address.</li>
<li><code>PORT</code> – The port number the broker is listening on.</li>
<li><code>USERNAME</code> – The authentication username for the MQTT broker.</li>
<li><code>PASSWORD</code> – The authentication password for the MQTT broker.</li>
<li><code>CLIENT_ID</code> – A unique identifier for the device connecting to the broker.</li>
<li><code>CLIENT_ID_SUFFIX</code> – A suffix shared among client IDs within the broker.</li>
<li><code>TOPIC</code> – The MQTT topic used for message communication.</li>
</ul>
</li>
<li>
<p>Click <strong>Save Settings</strong> to apply the changes and restart the device.</p>
</li>
</ol>
<h2 id="configuring-mqtt-in-node-red-with-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#configuring-mqtt-in-node-red-with-environment-variables"></a> Configuring MQTT in Node-RED with Environment Variables</h2>
<p>Now, let's explore how these environment variables can be used within Node-RED to configure the MQTT broker. Node-RED offers multiple ways to reference environment variables. Here are two primary methods to configure MQTT nodes using environment variables:</p>
<h3 id="1.-using-the-configuration-dialog%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#1.-using-the-configuration-dialog%3A"></a> 1. Using the Configuration Dialog:</h3>
<p>Environment variables can be directly referenced in the MQTT node properties using the <code>${ENV_NAME}</code> syntax as shown in the following images.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-config-with-env-PtmlSNrqRk-1092.avif 1092w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-config-with-env-PtmlSNrqRk-1092.webp 1092w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Setting up MQTT connection using environment variables." loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-config-with-env-PtmlSNrqRk-1092.jpeg" width="1092" height="892" /></picture>
<em>Setting up MQTT connection using environment variables.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-node-security-config-DsyHKJ2EIc-1100.avif 1100w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-node-security-config-DsyHKJ2EIc-1100.webp 1100w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring MQTT node security settings using environment variables in Node-RED." loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-node-security-config-DsyHKJ2EIc-1100.jpeg" width="1100" height="636" /></picture>
<em>Configuring MQTT node security settings using environment variables in Node-RED.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-out-config-pp7RI1X3PV-872.avif 872w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-out-config-pp7RI1X3PV-872.webp 872w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring the MQTT topic in Node-RED using environment variables." loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-out-config-pp7RI1X3PV-872.jpeg" width="872" height="652" /></picture>
<em>Configuring the MQTT topic in Node-RED using environment variables.</em></p>
<h3 id="2.-setting-values-dynamically-via-the-msg-object" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#2.-setting-values-dynamically-via-the-msg-object"></a> 2. Setting Values Dynamically via the <code>msg</code> Object</h3>
<p>In this approach, a <strong>Change node</strong> is used to retrieve environment variables and set the necessary MQTT configuration properties in the <code>msg</code> object. These properties include:</p>
<ul>
<li><code>msg.broker.broker</code> – The MQTT broker’s URL or IP address.</li>
<li><code>msg.action</code> – Must be set to <code>"connect"</code> when establishing a connection.</li>
<li><code>msg.broker.force</code> – Must be set to <code>true</code> to enforce the connection.</li>
<li><code>msg.broker.port</code> – The port number for the MQTT connection.</li>
<li><code>msg.broker.clientid</code> – The unique client identifier for the device.</li>
<li><code>msg.broker.username</code> – The MQTT username for authentication.</li>
<li><code>msg.broker.password</code> – The MQTT password for authentication.</li>
<li><code>msg.topic</code> – The MQTT topic to which the device will publish or subscribe.</li>
</ul>
<p>While the first method (direct reference in the MQTT node) is simpler and does not require additional nodes, it has limitations. It works well when there is only a single instance in the pipeline stage. However, when multiple instances exist within the same stage, the client ID, username, password, and topics often vary from device to device. The second method provides greater flexibility by dynamically adjusting these values, ensuring each device connects with the correct credentials and configurations. This approach makes the setup scalable and adaptable, eliminating the need for manual updates during deployment.</p>
<p>Unlike the first approach, which restricts direct combinations (e.g., string + environment variables or environment variables + environment variables), the second method enables dynamic modifications.</p>
<h4 id="ensuring-unique-mqtt-credentials-and-topics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#ensuring-unique-mqtt-credentials-and-topics"></a> Ensuring Unique MQTT Credentials and Topics</h4>
<p>As we mentioned in the multi-device deployment scenario, each device needs to establish its own connection to the MQTT broker while maintaining unique credentials and topics. If multiple devices in the same stage use identical configurations, connection conflicts—such as client ID duplication—may occur. To avoid these issues, each device must be assigned a unique client ID, username, password for security, and topic.</p>
<p>To ensure uniqueness, we can use the default environment variables available for each remote instance, such as:</p>
<ul>
<li><code>FF_DEVICE_NAME</code></li>
<li><code>FF_DEVICE_ID</code></li>
</ul>
<p>When generating the client ID for the MQTT broker, the device name (<code>FF_DEVICE_NAME</code>) can be used as the username. Since all client IDs share the same suffix, this suffix can be stored as a device-level environment variable (<code>CLIENT_ID_SUFFIX</code>). By combining both values, we can get the client ID of the device without manual intervention.</p>
<p>For the password, you can use the device ID (<code>FF_DEVICE_ID</code>), which is assigned when creating the client. Alternatively, you can set a common password for all clients by defining it as a device group-level environment variable.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/CHANGE-NODE-1-IqShVC1GrZ-800.avif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/CHANGE-NODE-1-IqShVC1GrZ-800.webp 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Using a Change node to dynamically set MQTT broker connection properties." loading="lazy" decoding="async" src="https://flowfuse.com/img/CHANGE-NODE-1-IqShVC1GrZ-800.jpeg" width="800" height="1078" /></picture>
<em>Using a Change node to dynamically set MQTT broker connection properties.</em></p>
<p>For topics, a combination of a string and <code>FF_DEVICE_NAME</code> can be used to ensure uniqueness.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/CHANGE-NODE-TOPIC-1-yCLBvfVPe4-510.avif 510w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/CHANGE-NODE-TOPIC-1-yCLBvfVPe4-510.webp 510w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring MQTT topics dynamically using a Change node in Node-RED." loading="lazy" decoding="async" src="https://flowfuse.com/img/CHANGE-NODE-TOPIC-1-yCLBvfVPe4-510.jpeg" width="510" height="540" /></picture>
<em>Configuring MQTT topics dynamically using a Change node in Node-RED.</em></p>
<p><em>Note: Ensure that the topic configuration is set dynamically when sending the payload, not when establishing the connection.</em></p>
<p>Once you have built your flow to connect to the intended MQTT broker using environment variables, deploy it and verify that it works as expected.</p>
<p>Also, if you need an example flow to test and explore in more detail, the following flow is provided. It demonstrates how to configure the MQTT connection dynamically using environment variables and, where needed, generate some settings by combining strings with environment variables.</p>
<div id="nr-flow-170" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow170 = "\n[{\"id\":\"0301bbe611a22e6d\",\"type\":\"cpu\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"msgCore\":false,\"msgOverall\":true,\"msgArray\":false,\"msgTemp\":false,\"x\":450,\"y\":380,\"wires\":[[\"0c0ee596549272d4\"]]},{\"id\":\"4b8f4fec3983d558\",\"type\":\"inject\",\"z\":\"aeaff62b91a987d4\",\"name\":\"Trigger every 5-second interval.\",\"props\":[],\"repeat\":\"5\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":190,\"y\":440,\"wires\":[[\"0301bbe611a22e6d\",\"e12765d187a7b502\",\"43e83b87e202428b\",\"552b49b55fbf8071\"]]},{\"id\":\"e12765d187a7b502\",\"type\":\"Memory\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"scale\":\"Gigabyte\",\"x\":440,\"y\":420,\"wires\":[[\"fe3d07cd9f4f1b20\"]]},{\"id\":\"43e83b87e202428b\",\"type\":\"Uptime\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"x\":440,\"y\":460,\"wires\":[[\"29541f3dafd85db9\"]]},{\"id\":\"552b49b55fbf8071\",\"type\":\"Loadavg\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"x\":440,\"y\":500,\"wires\":[[\"7b09b1eae14fac16\"]]},{\"id\":\"0c0ee596549272d4\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"CPU USAGE\",\"rules\":[{\"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\":650,\"y\":380,\"wires\":[[\"75445a62bac2e0c7\"]]},{\"id\":\"fe3d07cd9f4f1b20\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"MEMORY USAGE\",\"rules\":[{\"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\":670,\"y\":420,\"wires\":[[\"75445a62bac2e0c7\"]]},{\"id\":\"29541f3dafd85db9\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"SYSTEM UPTIME\",\"rules\":[{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.UPTIME\",\"pt\":\"msg\",\"to\":\"payload.uptime\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":670,\"y\":460,\"wires\":[[\"75445a62bac2e0c7\"]]},{\"id\":\"7b09b1eae14fac16\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"LOAD AVERAGE\",\"rules\":[{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.ONE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[0]\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIVE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[1]\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIFTEEN_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[2]\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":670,\"y\":500,\"wires\":[[\"75445a62bac2e0c7\"]]},{\"id\":\"75445a62bac2e0c7\",\"type\":\"join\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"mode\":\"custom\",\"build\":\"merged\",\"property\":\"data\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\\\n\",\"joinerType\":\"str\",\"useparts\":false,\"accumulate\":false,\"timeout\":\"\",\"count\":\"4\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":870,\"y\":440,\"wires\":[[\"acf3b791d682a13d\"]]},{\"id\":\"272ec74227a151db\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"Dynamically Configure MQTT Connection Using Environment Variables\",\"rules\":[{\"t\":\"set\",\"p\":\"action\",\"pt\":\"msg\",\"to\":\"connect\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"broker.broker\",\"pt\":\"msg\",\"to\":\"HOST\",\"tot\":\"env\"},{\"t\":\"set\",\"p\":\"broker.port\",\"pt\":\"msg\",\"to\":\"PORT\",\"tot\":\"env\"},{\"t\":\"set\",\"p\":\"broker.clientid\",\"pt\":\"msg\",\"to\":\"${FF_DEVICE_NAME}${Client_ID_SUFFIX}\",\"tot\":\"env\"},{\"t\":\"set\",\"p\":\"broker.username\",\"pt\":\"msg\",\"to\":\"broker.clientid\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"broker.password\",\"pt\":\"msg\",\"to\":\"FF_DEVICE_ID\",\"tot\":\"env\"},{\"t\":\"set\",\"p\":\"broker.force\",\"pt\":\"msg\",\"to\":\"true\",\"tot\":\"bool\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":820,\"y\":580,\"wires\":[[\"4860cc23dfca9720\"]]},{\"id\":\"4860cc23dfca9720\",\"type\":\"mqtt out\",\"z\":\"aeaff62b91a987d4\",\"name\":\"\",\"topic\":\"\",\"qos\":\"0\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"f484702903e298e7\",\"x\":1270,\"y\":580,\"wires\":[]},{\"id\":\"acf3b791d682a13d\",\"type\":\"change\",\"z\":\"aeaff62b91a987d4\",\"name\":\"Set payload and topic\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"factory/line2/${FF_DEVICE_NAME}\",\"tot\":\"env\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1080,\"y\":440,\"wires\":[[\"4860cc23dfca9720\"]]},{\"id\":\"79dfc62b4b21d20b\",\"type\":\"inject\",\"z\":\"aeaff62b91a987d4\",\"name\":\"Trigger on deploy\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":210,\"y\":580,\"wires\":[[\"272ec74227a151db\"]]},{\"id\":\"f484702903e298e7\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"localhost\",\"port\":1883,\"clientid\":\"\",\"autoConnect\":false,\"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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow170.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-170') })</script>
<h2 id="setting-environment-variables-for-a-device-group" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#setting-environment-variables-for-a-device-group"></a> Setting Environment Variables for a Device Group</h2>
<p>After confirming that your flow in the development stage remote instance works as expected and connects to the broker correctly, the next step is to add environment variables.</p>
<p>If your target stage has only a single instance, you can set the environment variables at the instance level, as shown in the image below, and use the first method discussed earlier. This approach is straightforward and efficient for configuring a single instance using environment variables.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/env-vars-blur--zCejy6xIN-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/env-vars-blur--zCejy6xIN-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Setting environment variables at the instance level in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/env-vars-blur--zCejy6xIN-1920.jpeg" width="1920" height="1132" /></picture>
<em>Setting environment variables at the instance level in FlowFuse.</em></p>
<p>However, when multiple remote instances exist within the same stage, such as in a device group, configuring them individually can be impractical, especially when most settings remain the same. To simplify this process, FlowFuse allows you to set environment variables at the device group level, and you can use the second method to make the configuration quick and easy while using remote instance default environment variables.</p>
<p>To add the device group level environment variables follow the steps:</p>
<ol>
<li>
<p>Go to your <strong>device group’s settings</strong> in the FlowFuse platform.</p>
</li>
<li>
<p>Add the following environment variables with the appropriate values:</p>
<ul>
<li><code>HOST</code></li>
<li><code>PORT</code></li>
<li><code>CLIENT_ID_SUFFIX</code></li>
</ul>
</li>
<li>
<p>Click <strong>Save Settings</strong> to apply the changes.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/env-settings-mFLZKMVfHS-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/env-settings-mFLZKMVfHS-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring environment variables at the device group level." loading="lazy" decoding="async" src="https://flowfuse.com/img/env-settings-mFLZKMVfHS-1920.jpeg" width="1920" height="713" /></picture>
<em>Configuring environment variables at the device group level.</em></p>
<p>With this approach, you do not need to set environment variables separately for each instance. Instead, the environment variables defined at the device group level will apply to all remote instances within the group, ensuring a consistent and efficient configuration.</p>
<h2 id="deploying-flow-with-stage-specific-configurations-via-devops-pipeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#deploying-flow-with-stage-specific-configurations-via-devops-pipeline"></a> Deploying Flow with Stage-Specific Configurations via DevOps Pipeline</h2>
<p>Now that everything is set up, trigger the deployment pipeline for the development stage. This will push the flow and settings to the next-stage instances while preserving the existing environment variables. Before applying the new configuration, all remote instances in the target stage will restart automatically.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/devops-pipeline-KcVlfhtPRv-1460.avif 1460w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/devops-pipeline-KcVlfhtPRv-1460.webp 1460w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Deploying Node-RED flows using FlowFuse's DevOps pipeline." loading="lazy" decoding="async" src="https://flowfuse.com/img/devops-pipeline-KcVlfhtPRv-1460.jpeg" width="1460" height="868" /></picture>
<em>Deploying Node-RED flows using FlowFuse's DevOps pipeline.</em></p>
<p>If everything is configured correctly, the MQTT nodes in each remote Node-RED instance will connect to the appropriate broker configured at the device group level.</p>
<h2 id="monitoring-mqtt-topic-hierarchy-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#monitoring-mqtt-topic-hierarchy-with-flowfuse"></a> Monitoring MQTT Topic Hierarchy with FlowFuse</h2>
<p>Once the Node-RED flow is deployed on all remote instances in the device group and the MQTT nodes in each instance are connected to the broker, you can monitor and manage all topics across all brokers directly in FlowFuse.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-monitoring-aXWOCtMtz--1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-monitoring-aXWOCtMtz--1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Monitoring MQTT topics in FlowFuse across multiple brokers." loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-monitoring-aXWOCtMtz--1920.jpeg" width="1920" height="599" /></picture>
<em>Monitoring MQTT topics in FlowFuse across multiple brokers.</em></p>
<p>Watch this short video to learn how to bring your own brokers for topic monitoring in FlowFuse: <a href="https://youtube.com/shorts/-8TPXb0h0vA?si=wi3wghi4vUWlXJTZ">https://youtube.com/shorts/-8TPXb0h0vA?si=wi3wghi4vUWlXJTZ</a></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/managing-mqtt-connections-at-scale-in-flowfuse/#conclusion"></a> Conclusion</h2>
<p>FlowFuse makes it easy to deploy Node-RED flows while ensuring each stage connects to the right MQTT broker. By using environment variables at both the instance and device group levels, you can automate MQTT configurations, reducing manual setup and ensuring consistency.</p>
<p><a href="https://app.flowfuse.com/account/create/">Get Started Now</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/FlowFuse 2.15: Personal Node Collections, Smart Schema Suggestions and more control in DevOps Pipelines!Start building out your own collection of private nodes and Javascript libraries for Node-RED with our new Custom Node catalogues2025-03-12T00:00:00ZJoe Pavitt<p>Our three focal points for the latest release of FlowFuse have been:</p>
<ul>
<li><strong>Collaborative Development:</strong> Our new <strong>Custom Node Catalogues</strong> provides a place for you to centralise your own private nodes and Javascript libraries for Node-RED.</li>
<li><strong>Full Stack Applications:</strong> We've added our new <strong>"Smart Schema Suggestions"</strong> feature for automatically generating documentation for your MQTT Brokers.</li>
<li><strong>DevOps:</strong> We've improved Pipelines now to let you deploy to Device Groups in the middle of a Pipeline, this is just the first step in a wider set of improvements to come for DevOps Pipelines.</li>
</ul>
<!--more-->
<h2 id="custom-node-catalogues" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#custom-node-catalogues"></a> Custom Node Catalogues</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-custon-catalog-jzEm54qWu7-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-custon-catalog-jzEm54qWu7-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the new "Custom Nodes" view in Team Library"" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-custon-catalog-jzEm54qWu7-1920.jpeg" width="1920" height="1063" /></picture>
<em>Screenshot of the new "Custom Nodes" view in Team Library</em></p>
<h3 id="pain-point" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#pain-point"></a> Pain Point</h3>
<p>Many of our customers have very mature Node-RED setups, including large collections of their own, custom node packages that they have built. These packages range significantly in purpose, from small utility functions, to custom hardware integrations.</p>
<p>Until now, to utilise these packages within FlowFuse, you would either need to publish to the <a href="https://flows.nodered.org/search?type=node">public Node-RED Community Catalogue</a>, meaning all of your nodes are available to anyone in the world using Node-RED, or you had to setup and manage your own private NPM registry, which is very time consuming and requires deeper technical knowledge.</p>
<h3 id="now-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#now-in-flowfuse"></a> Now in FlowFuse</h3>
<p>With our new update, every Team- and Enterprise-Tier Library in FlowFuse has an in-built private Node collection, ready for you to go. This means that you can push your custom node packages directly to that registry, and make it available to all of your Instances running in FlowFuse within seconds. This comes built in with full version control and all of the great update mechanisms that you already have in Node-RED.</p>
<p>This new feature also integrates seamlessly ith our <a href="https://flowfuse.com/docs/user/bill-of-materials/">Bill of Materials</a> view, providing you the single source of truth to all of your Instance's dependencies in one-place, making it easier to audit and manage your Node-RED instances and their dependencies.</p>
<h2 id="mqtt-broker-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#mqtt-broker-integration"></a> MQTT Broker Integration</h2>
<h3 id="smart-schema-suggestions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#smart-schema-suggestions"></a> Smart Schema Suggestions</h3>
<iframe width="100%" height="400" src="https://www.youtube.com/embed/bNeTDJUZ1So?si=LFKiLrCrH6JUq3DH" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" style="margin-bottom: 12px;"></iframe>
<p>We are very pleased to announce our new "Smart Suggestions" feature. When working with an MQTT Broker for live data, it can be difficult to understand the structure of the data and topics that are being used. We recently released the new "Hierarchy" view, showing the topic structure present on a Broker, and we've now evolved that even further.</p>
<p>With Smart Suggestions, you will be presented with proposals as to what FlowFuse <em>thinks</em> your payload schema is, based on the data that is being published to your Broker. If you approve these suggestions, then FlowFuse wll start constructing a schema to represent the traffic on your Broker, and make that available in our new "Schema Documentation" view.</p>
<p>This will save you days of effort in documenting your Broker, and provide a single source of truth for your schema to ensure your team is always on the same page when working with your MQTT data.</p>
<h3 id="searching-for-topics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#searching-for-topics"></a> Searching for Topics</h3>
<p>A very nice, small, improvement we've made to the Broker view too is the ability to search for topics to quickly find the topics of interest. This is a small but very useful feature, especially for those with large numbers of topics on their Broker, enabling you to find the topics of interest much faster.</p>
<h2 id="devops-pipelines-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#devops-pipelines-improvements"></a> DevOps Pipelines Improvements</h2>
<p>Last year, we announced the introduction of Device Groups. For those that are not familiar with Device Groups, this feature enables users to logically group their Remote Instances so that they can be targeted in a DevOps Pipeline, facilitating streamlined and efficient deployments across your fleet of devices, rolling out a single flow to thousands of Remote Instances within seconds.</p>
<p>Previously, you could only use a Group as the <em>last</em> stage in a Pipeline. With this update, you can now have Groups at other stages in a pipeline. For example, you may have a group of test Remote Instances that you want to push updates to, before pushing them out to your larger group of production Instances.</p>
<p>This improvement not only saves time but also enhances the consistency and reliability of Remote Instance management across your fleet.</p>
<h2 id="removing-credit-card-requirement-for-free-tier" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#removing-credit-card-requirement-for-free-tier"></a> Removing Credit Card Requirement for Free Tier</h2>
<p>When we recently introduced the new Free Tier for FlowFuse Cloud, we had some legacy architecture in place that meant that, whilst it was entirely free, the Free Tier still require you to put in credit card details.</p>
<p>This is something we've completely overhauled in the new version of FlowFuse, so now, you can just sign up and get started with building your own Remote Instances without the need for any credit card details. It was deployed to FlowFuse Cloud a week ago, and is already proving popular with new users. We can't wait to see what everyone builds on the free tier.</p>
<h2 id="flowfuse-dashboard-authentication-now-includes-user's-role" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#flowfuse-dashboard-authentication-now-includes-user's-role"></a> FlowFuse Dashboard Authentication Now Includes User's Role</h2>
<p>An enhancement to FlowFuse's underlying Node-RED launcher is the inclusion of the FlowFuse user's role in the <code>user</code> object return by the FlowFuse User Addon. This means you can start building your own RBAC (Role Based Access Control) into your own Node-RED/Dashboard applications, without the need to define roles and access levels directly within your Node-RED flows.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-user-role-Hsa2rjirbL-534.avif 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-user-role-Hsa2rjirbL-534.webp 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img style="margin: auto" width="300px" alt="Screenshot showing the inclusion of the user's role within Dashboard's "user" object" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-user-role-Hsa2rjirbL-534.jpeg" height="438" /></picture>
<em>Screenshot showing the inclusion of the user's role within Dashboard's "user" object</em></p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#what-else-is-new%3F"></a> What Else Is New?</h2>
<p>For a full list of everything that went into our 2.15 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.15.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/03/flowfuse-release-2-15/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/Monitoring Device Health and Performance at Scale with FlowFuseTrack and Optimize Edge Device Performance with Node-RED and FlowFuse.2025-02-21T00:00:00ZSumit Shinde<p>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 <a href="https://transformainsights.com/news/edge-computing-rapid-growth-iot#:~:text=New%20Transforma%20Insights%20reports%20covering,edge%20capabilities%20in%20IoT%20devices.">various reports</a>. As these devices become critical for automation and data processing, monitoring their health is essential to ensure reliability and efficiency.</p>
<!--more-->
<p>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.</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/43te5aD1RRw?si=5X2XbER_-ZQMLZOb" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="what-is-device-health-monitoring%2C-and-why-is-it-important%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#what-is-device-health-monitoring%2C-and-why-is-it-important%3F"></a> What is Device Health Monitoring, and Why is it Important?</h2>
<p>Edge devices power IoT and automation, handling communication and data processing. As their numbers grow, ensuring they run efficiently is crucial.</p>
<p>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.</p>
<p>For example, in manufacturing, edge devices connect machines to cloud systems for real-time data. If a device fails, production can be impacted.</p>
<p>Regular monitoring helps detect issues early, prevents downtime, and keeps devices running smoothly.</p>
<h2 id="getting-started-with-monitoring-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#getting-started-with-monitoring-devices"></a> Getting Started with Monitoring Devices</h2>
<p>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.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#prerequisites"></a> Prerequisites</h3>
<p>Before you begin, ensure you have the following:</p>
<ol>
<li><strong>Running Node-RED Instance:</strong> You need a running Node-RED instance on the device you want to monitor. The easiest way to set this up is with the <a href="https://flowfuse.com/product/device-agent/">FlowFuse Device Agent</a>, 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.</li>
</ol>
<p>For a step-by-step installation guide, refer to the <a href="https://flowfuse.com/docs/device-agent/quickstart/">FlowFuse Device Agent Quickstart</a>.</p>
<p>If you haven’t yet signed up for a FlowFuse account, <a href="https://app.flowfuse.com/account/create/?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">sign up now</a>.</p>
<ol>
<li><strong>Required Node-RED Nodes:</strong> To collect system data and display it on a dashboard, install the following Node-RED nodes via the <a href="https://nodered.org/docs/user-guide/editor/palette/manager">Node-RED Palette Manager</a>:</li>
</ol>
<ul>
<li><code>node-red-contrib-os</code>: Retrieves system information such as memory, uptime, and load.</li>
<li><code>node-red-contrib-cpu</code>: Monitors CPU usage.</li>
<li><code>@flowfuse/node-red-dashboard</code>: Provides UI components for visualizing system metrics.</li>
<li><code>node-red-contrib-moment</code>: Formats uptime duration in a human-readable format.</li>
</ul>
<h3 id="collecting-cpu-and-system-metrics-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#collecting-cpu-and-system-metrics-with-node-red"></a> Collecting CPU and System Metrics with Node-RED</h3>
<p>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.</p>
<p>Let’s break it down step by step.</p>
<h4 id="collecting-cpu-usage-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#collecting-cpu-usage-data"></a> Collecting CPU Usage Data</h4>
<p>To start, let’s capture CPU usage in real time:</p>
<ol>
<li>Drag a <strong>CPU</strong> node from the "Performance" category onto the canvas.</li>
<li>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.</li>
<li>Add an <strong>Inject</strong> 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.</li>
<li>Add a <strong>Debug</strong> node and connect it to the output of the CPU node. This lets you view CPU data in the debug pane.</li>
<li><strong>Click Deploy</strong> in the top-right corner of the Node-RED editor.</li>
</ol>
<p>Your debug pane will now start showing live CPU usage data:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/cpu-usage-YopWsPpI2T-1508.avif 1508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/cpu-usage-YopWsPpI2T-1508.webp 1508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="An image showing the flow that gathers CPU usage data and prints it in the debug pane" loading="lazy" decoding="async" src="https://flowfuse.com/img/cpu-usage-YopWsPpI2T-1508.jpeg" width="1508" height="522" /></picture>
<em>An image showing the flow that gathers CPU usage data and prints it in the debug pane</em></p>
<h4 id="monitoring-memory-usage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#monitoring-memory-usage"></a> Monitoring Memory Usage</h4>
<p>Next, let’s track memory consumption:</p>
<ol>
<li>Drag a Memory node onto the canvas and double-click it.</li>
<li>Choose the unit for memory display (e.g., gigabytes for easier readability).</li>
<li>Connect the Memory node’s input to the Inject node’s output.</li>
<li>Connect the Memory node’s output to the existing Debug node.</li>
<li>Click Deploy to start monitoring.</li>
</ol>
<p>Once deployed, you will see a structured object in the debug pane containing along with cpu usage:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/memory-usage-XVNiXoFx2h-1514.avif 1514w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/memory-usage-XVNiXoFx2h-1514.webp 1514w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="An image showing the flow that gathers memory usage data and prints it in the debug pane" loading="lazy" decoding="async" src="https://flowfuse.com/img/memory-usage-XVNiXoFx2h-1514.jpeg" width="1514" height="528" /></picture>
<em>An image showing the flow that gathers memory usage data and prints it in the debug pane</em></p>
<ul>
<li>totalmem: Total available memory</li>
<li>freemem: Free memory</li>
<li>memusage: Current memory usage</li>
</ul>
<h4 id="tracking-system-uptime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#tracking-system-uptime"></a> Tracking System Uptime</h4>
<p>Monitoring uptime helps detect unexpected reboots and ensures system stability.</p>
<ol>
<li>Drag an Uptime node onto the canvas.</li>
<li>Connect its input to the Inject node’s output.</li>
<li>Connect its output to the Debug node.</li>
<li>Click Deploy to activate uptime tracking.</li>
</ol>
<p>Each time the Inject node triggers, the debug pane will display the uptime in seconds and CPU and memory usage.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/uptime-onl6vwh8dN-1526.avif 1526w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/uptime-onl6vwh8dN-1526.webp 1526w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="An image showing the flow that gathers system uptime data and prints it in the debug pane" loading="lazy" decoding="async" src="https://flowfuse.com/img/uptime-onl6vwh8dN-1526.jpeg" width="1526" height="592" /></picture>
<em>An image showing the flow that gathers system uptime data and prints it in the debug pane</em></p>
<h4 id="analyzing-load-average" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#analyzing-load-average"></a> Analyzing Load Average</h4>
<p>To understand how busy your system has been over time, let’s analyze the load average:</p>
<ol>
<li>Drag a <strong>Loadavg</strong> node onto the canvas.</li>
<li>Connect its input to the Inject node’s output.</li>
<li>Connect its output to the Debug node.</li>
<li>Click Deploy to start tracking.</li>
</ol>
<p>This will give you three key metrics:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/load-avg-YR-rliG8t2-1540.avif 1540w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/load-avg-YR-rliG8t2-1540.webp 1540w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="An image showing the flow that gathers system load average data and prints it in the debug pane" loading="lazy" decoding="async" src="https://flowfuse.com/img/load-avg-YR-rliG8t2-1540.jpeg" width="1540" height="722" /></picture></p>
<ul>
<li>1-minute load average: Immediate system load</li>
<li>5-minute load average: Recent short-term trend</li>
<li>15-minute load average: Long-term system trend</li>
</ul>
<p>If these values remain consistently high, your system may struggle under excessive demand, signaling a need for optimization or additional processing power.</p>
<p>With these metrics in place, you have a solid foundation for real-time system monitoring.</p>
<h3 id="sharing-data-across-different-node-red-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#sharing-data-across-different-node-red-instances"></a> Sharing Data Across Different Node-RED Instances</h3>
<p>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.</p>
<p>To send data between multiple Node-RED instances we can use <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/">FlowFuse's Project Nodes</a>.</p>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-168" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow168 = "\n[{\"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\":[],\"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\":[{\"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\":[{\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"data\",\"pt\":\"msg\",\"to\":\"{}\",\"tot\":\"json\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.ONE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[0]\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIVE_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[1]\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.LOAD_AVERAGE.FIFTEEN_MIN\",\"pt\":\"msg\",\"to\":\"payload.loadavg[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\":[{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow168.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-168') })</script>
<p>Let's understand the flow.</p>
<p>In the flow above, four Change nodes are used, each connected to the output of the <strong>CPU</strong>, <strong>Memory</strong>, <strong>Uptime</strong>, and <strong>Loadavg</strong> nodes. As mentioned earlier, these nodes provide their data separately as <code>msg.payload</code>. We use Change nodes to modify the message structure before sending the data to ensure a more structured and organized format.</p>
<p>Next, a Join node merges the <code>msg.data</code> objects from all Change nodes into a single data object. After that, another Change node assigns this combined object to <code>msg.payload</code>.</p>
<p>The final combined object appears as shown in the image below:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/combine-object-IRhmVaYAn1-502.avif 502w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/combine-object-IRhmVaYAn1-502.webp 502w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Combined object containing system data such as CPU usage, memory usage, uptime, and load average." loading="lazy" decoding="async" src="https://flowfuse.com/img/combine-object-IRhmVaYAn1-502.jpeg" width="502" height="498" /></picture>
<em>Combined object containing system data such as CPU usage, memory usage, uptime, and load average.</em></p>
<p>To share this data with other Node-RED instances, we use the <strong>Project Out</strong> 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.</p>
<p>In this Project node, we broadcast the message across all instances in the team using <code>${FF_DEVICE_NAME}</code> as the topic—an environment variable automatically created in all FlowFuse instances.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/env-FTVf9PiuHo-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/env-FTVf9PiuHo-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the environment variables of Raspberry Pi devices with their values." loading="lazy" decoding="async" src="https://flowfuse.com/img/env-FTVf9PiuHo-1920.jpeg" width="1920" height="679" /></picture>
<em>Image showing the environment variables of Raspberry Pi devices with their values.</em></p>
<p>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.</p>
<h3 id="visualizing-data-with-the-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#visualizing-data-with-the-flowfuse-dashboard"></a> Visualizing Data with the FlowFuse Dashboard</h3>
<p>Now that the data is being broadcasted, it can be used to build a simple dashboard that visualizes it with different types of charts.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-monitoring-device-SgX0UJIoFi-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-monitoring-device-SgX0UJIoFi-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Dashboard monitoring device CPU usage, memory uptime, and load average" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-monitoring-device-SgX0UJIoFi-1920.jpeg" width="1920" height="1098" /></picture>
<em>Dashboard monitoring device CPU usage, memory uptime, and load average</em></p>
<p>Ensure that a separate <strong>Hosted Instance</strong> has been created in the same Team where the hardware is registered. This instance will be used to deploy the dashboard.</p>
<h4 id="setting-up-the-data-source" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#setting-up-the-data-source"></a> Setting Up the Data Source</h4>
<ol>
<li>Drag the <strong>Project In</strong> node onto the canvas.</li>
<li>Double-click on it and select "Listen for broadcast messages from".</li>
<li>Choose "All instances and devices" from the "Source" dropdown menu.</li>
<li>Enter the device name in the topic field, ensuring it matches exactly with the <code>${FF_DEVICE_NAME}</code> device environment variable.</li>
<li>Click <strong>Done</strong>.</li>
</ol>
<h4 id="memory-usage-visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#memory-usage-visualization"></a> Memory Usage Visualization</h4>
<ol>
<li>Drag two <strong>Change</strong> nodes onto the canvas.</li>
<li>Double-click on the first <strong>Change</strong> node. Set <code>msg.payload</code> to:
<div style="position: relative" id="code-container-370">
<pre class="language-json"><code id="code-370" class="language-json">payload.MEMORY_USAGE.totalmem - payload.MEMORY_USAGE.freemem</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-370" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>Set <code>msg.topic</code> to "USED MEMORY" and click <strong>Done</strong>.</li>
<li>Double-click on the second <strong>Change</strong> node, Set <code>msg.payload</code> to:
<div style="position: relative" id="code-container-381">
<pre class="language-json"><code id="code-381" class="language-json">msg.payload.MEMORY_USAGE.freemem</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-381" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>Set <code>msg.topic</code> to "Free Memory" and click <strong>Done</strong>.</li>
<li>Drag a <strong>ui-chart</strong> widget onto the canvas.</li>
<li>Double-click on the widget and create a new Group.</li>
<li>Set the chart type to "Pie" and action to "Append".</li>
<li>Set X to <code>msg.topic</code> and leave Y empty.</li>
<li>Click <strong>Done</strong>.</li>
<li>Connect the nodes as follows:</li>
</ol>
<p>Project In node → Change nodes → ui-chart widget</p>
<h4 id="cpu-usage-visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#cpu-usage-visualization"></a> CPU Usage Visualization</h4>
<ol>
<li>Drag a Change node onto the canvas.</li>
<li>Double-click on the node. Set <code>msg.payload</code> to:
<div style="position: relative" id="code-container-435">
<pre class="language-json"><code id="code-435" class="language-json">$round(payload.CPU_USAGE<span class="token punctuation">,</span> <span class="token number">2</span>)</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-435" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>Click Done.</li>
<li>Drag a ui-gauge widget onto the canvas.</li>
<li>Double-click on the widget and create a new group.</li>
<li>Set the height and size.</li>
<li>Select "3/4 gauge" with a rounded style.</li>
<li>Set the range from 0 to 100.</li>
<li>Add three segments with colors: 0 (Green), 50 (Yellow), 80 (Red).</li>
<li>Set the label to "CPU" and unit to %.</li>
<li>Click Done.</li>
<li>Connect the nodes as follows:</li>
</ol>
<p>Project In node → Change node → ui-gauge widget</p>
<h4 id="system-uptime-visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#system-uptime-visualization"></a> System Uptime Visualization</h4>
<ol>
<li>Drag the Humanizer node onto the canvas.</li>
<li>Double-click on the node and enter "UPTIME" in the input variable field.</li>
<li>Click Done.</li>
<li>Drag a Change node onto the canvas.</li>
<li>Double-click on the node. Set <code>msg.payload</code> to:
<div style="position: relative" id="code-container-519">
<pre class="language-json"><code id="code-519" class="language-json">msg.payload.humanized</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-519" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>Click Done.</li>
<li>Drag a ui-text widget onto the canvas.</li>
<li>Double-click on it and create a new group.</li>
<li>Select the correct layout.</li>
<li>Check the "Apply Styles" option and select the color, font, and size that best suits your needs.</li>
<li>Click Done.</li>
<li>Connect the nodes as follows:</li>
</ol>
<p>Project In node → Humanizer node → Change node → ui-text widget</p>
<p>Below is the complete dashboard flow, which visualizes the system data we collected.</p>
<div id="nr-flow-169" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow169 = "\n[{\"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\":[{\"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\":[\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"textColor\":[\"#666666\"],\"textColorDefault\":true,\"gridColor\":[\"#e5e5e5\"],\"gridColorDefault\":true,\"width\":\"3\",\"height\":\"3\",\"className\":\"\",\"interpolation\":\"linear\",\"x\":520,\"y\":140,\"wires\":[[]]},{\"id\":\"acd675febbdadc6f\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"Used Memory\",\"rules\":[{\"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\":[{\"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\":[{\"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\":[]},{\"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\":\"\",\"layout\":\"col-center\",\"style\":true,\"font\":\"\",\"fontSize\":\"99\",\"color\":\"#0056d6\",\"wrapText\":false,\"className\":\"\",\"x\":680,\"y\":340,\"wires\":[]},{\"id\":\"9d237ff676ad6083\",\"type\":\"change\",\"z\":\"797e084100cec864\",\"name\":\"\",\"rules\":[{\"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\":[{\"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\":[{\"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\":[{\"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\":[\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#a347e1\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"textColor\":[\"#666666\"],\"textColorDefault\":true,\"gridColor\":[\"#e5e5e5\"],\"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\":\"/macos\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"5075a7d8e4947586\",\"breakpoints\":[{\"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\":\"6c8450c52cafa145\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow169.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-169') })</script>
<h3 id="scaling-device-monitoring-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#scaling-device-monitoring-with-flowfuse"></a> Scaling Device Monitoring with FlowFuse</h3>
<p>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.</p>
<h4 id="creating-device-group" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#creating-device-group"></a> Creating Device Group</h4>
<ol>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/option-add-device-group-qkNcZpOCjm-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/option-add-device-group-qkNcZpOCjm-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Showing the option to switch to "Device Groups" and the "Add Device Group" button." loading="lazy" decoding="async" src="https://flowfuse.com/img/option-add-device-group-qkNcZpOCjm-1920.jpeg" width="1920" height="1094" /></picture><br />
<em>Showing the option to switch to "Device Groups" and the "Add Device Group" button.</em></p>
<ol>
<li>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".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-group-form-create-DV5P8oak5H-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-group-form-create-DV5P8oak5H-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Form to Create a Device Group: Enter the group name and description " loading="lazy" decoding="async" src="https://flowfuse.com/img/device-group-form-create-DV5P8oak5H-1920.jpeg" width="1920" height="681" /></picture><br />
<em>Form to Create a Device Group: Enter the group name and description</em></p>
<ol start="2">
<li>Click on the newly created group and then click the "Edit" button at the top-right.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-device-group-c8WIqAdM8X-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-device-group-c8WIqAdM8X-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the edit button to be clicked on." loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-device-group-c8WIqAdM8X-1920.jpeg" width="1920" height="596" /></picture><br />
<em>Image showing the edit button to be clicked on.</em></p>
<ol start="3">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-group-device-adding-hA5edOYslL-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-group-device-adding-hA5edOYslL-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Interface to select the devices that need to be added to the group, along with the 'Add Devices' button." loading="lazy" decoding="async" src="https://flowfuse.com/img/device-group-device-adding-hA5edOYslL-1920.jpeg" width="1920" height="648" /></picture><br />
<em>Interface to select the devices that must be added to the group, along with the 'Add Devices' button.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/save-changes-to-add-devices-hondp_iDhI-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/save-changes-to-add-devices-hondp_iDhI-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Showing the selected devices we chose to add, along with the 'Save Changes' button." loading="lazy" decoding="async" src="https://flowfuse.com/img/save-changes-to-add-devices-hondp_iDhI-1920.jpeg" width="1920" height="695" /></picture><br />
<em>Showing the selected devices we chose to add, along with the 'Save Changes' button.</em></p>
<h4 id="creating-snapshot" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#creating-snapshot"></a> Creating Snapshot</h4>
<ol>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/create-snapshot-FNtGEMUOuD-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/create-snapshot-FNtGEMUOuD-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Showing the option to switch to "Version history" and the "Create Snapshot" button." loading="lazy" decoding="async" src="https://flowfuse.com/img/create-snapshot-FNtGEMUOuD-1920.jpeg" width="1920" height="733" /></picture><br />
<em>Showing the option to switch to "Version history" and the "Create Snapshot" button.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/set-active-snapshot-c61d8CqOgg-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/set-active-snapshot-c61d8CqOgg-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt=""Showing the form to create a snapshot and the "Set as Target" option."" loading="lazy" decoding="async" src="https://flowfuse.com/img/set-active-snapshot-c61d8CqOgg-1920.jpeg" width="1920" height="731" /></picture><br />
<em>Showing the form to create a snapshot and the "Set as Target" option.</em></p>
<p>If you want to learn more about snapshots, you can read our article <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/">Using Snapshots for Version Control in Node-RED with FlowFuse</a>.</p>
<h4 id="creating-a-devops-pipeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#creating-a-devops-pipeline"></a> Creating a DevOps Pipeline</h4>
<ol>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/add-pipeline-button-HxPo7x1ny5-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/add-pipeline-button-HxPo7x1ny5-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Add Pipeline' button." loading="lazy" decoding="async" src="https://flowfuse.com/img/add-pipeline-button-HxPo7x1ny5-1920.jpeg" width="1920" height="523" /></picture><br />
<em>Image showing the 'Add Pipeline' button.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pipeline-creation-form-6qgVkZfh8T-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/pipeline-creation-form-6qgVkZfh8T-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the form to create a pipeline by entering a name." loading="lazy" decoding="async" src="https://flowfuse.com/img/pipeline-creation-form-6qgVkZfh8T-1920.jpeg" width="1920" height="561" /></picture><br />
<em>Image showing the form to create a pipeline by entering a name.</em></p>
<ol start="2">
<li>In the newly added pipeline, click "Add Stage".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/adding-stage-button-dn_OyFObMU-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/adding-stage-button-dn_OyFObMU-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the button to add a stage." loading="lazy" decoding="async" src="https://flowfuse.com/img/adding-stage-button-dn_OyFObMU-1920.jpeg" width="1920" height="700" /></picture><br />
<em>Image showing the button to add a stage.</em></p>
<ol start="3">
<li>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".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/rpi-stage-zPgPIecDxi-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/rpi-stage-zPgPIecDxi-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the form to add a stage, where a stage is being added for a Raspberry Pi remote instance." loading="lazy" decoding="async" src="https://flowfuse.com/img/rpi-stage-zPgPIecDxi-1920.jpeg" width="1920" height="892" /></picture><br />
<em>Image showing the form to add a stage, where a stage is being added for a Raspberry Pi remote instance.</em></p>
<ol start="4">
<li>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."</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/production-with-deviec-group-wo52QuJeJE-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/production-with-deviec-group-wo52QuJeJE-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the form to add a stage, where a stage is being added for a Device Group." loading="lazy" decoding="async" src="https://flowfuse.com/img/production-with-deviec-group-wo52QuJeJE-1920.jpeg" width="1920" height="755" /></picture><br />
<em>Image showing the form to add a stage, where a stage is being added for a Device Group.</em></p>
<ol start="5">
<li>Before moving further, ensure all devices are in fleet mode.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/fleet-mode-QZnuZn2jkK-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/fleet-mode-QZnuZn2jkK-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the fleet mode status of the device (disabling the developer mode option will set the device to fleet mode)." loading="lazy" decoding="async" src="https://flowfuse.com/img/fleet-mode-QZnuZn2jkK-1920.jpeg" width="1920" height="199" /></picture><br />
<em>Image showing the fleet mode status of the device (disabling the developer mode option will set the device to fleet mode).</em></p>
<ol start="6">
<li>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.</li>
</ol>
<p>To learn more about DevOps pipelines, read the article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/">Creating and Automating DevOps Pipelines for Node-RED in Industrial Environments</a>.</p>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#visualizing-data-with-the-flowfuse-dashboard">these steps</a> again for each device, and you will have a centralized dashboard monitoring thousands of devices live.</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/43te5aD1RRw?si=5X2XbER_-ZQMLZOb" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/monitoring-system-health-performance-scale-flowfuse/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/Interacting with Arduino using Node-REDControl and Automate Arduino with Node-RED2025-02-12T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>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!</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/FTuxOy16nwo?si=i7wds6zH0Hpo0TTM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#prerequisites"></a> Prerequisites</h2>
<p>To follow this tutorial, you'll need the following:</p>
<ul>
<li>Arduino Board: The hardware you'll be using for this project.</li>
<li>USB cable: To connect the Arduino to your computer.</li>
<li>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</li>
<li>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.</li>
</ul>
<h2 id="getting-started-with-arduino-and-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#getting-started-with-arduino-and-node-red"></a> Getting Started with Arduino and Node-RED</h2>
<p>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.</p>
<h3 id="step-1%3A-running-node-red-on-the-device-connected-to-arduino" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-1%3A-running-node-red-on-the-device-connected-to-arduino"></a> Step 1: Running Node-RED on the Device connected to Arduino</h3>
<p>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.</p>
<p>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.</p>
<p>For a step-by-step guide on installing and running the FlowFuse device agent, refer to the official documentation: <a href="https://flowfuse.com/docs/device-agent/quickstart/">FlowFuse Device Agent Quickstart</a>. By the way, we also offer a <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/">free tier</a> that lets you manage up to two edge devices for free. <a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Interacting%20with%20Arduino%20using%20Node-RED">Sign up today</a>!</p>
<h3 id="step-2%3A-downloading-firmata-protocol-setup-to-arduino." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-2%3A-downloading-firmata-protocol-setup-to-arduino."></a> Step 2: Downloading Firmata protocol setup to Arduino.</h3>
<p><a href="https://github.com/firmata/protocol">Firmata</a> is a protocol for communicating between an Arduino (and other microcontrollers) and the host computer, providing direct access to the IO pins.</p>
<p>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.</p>
<p><strong>Setting up Arduino IDE and Download the setup from examples:</strong></p>
<ol>
<li>Open the Arduino IDE on your computer.</li>
<li>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.</li>
<li>Go to File → Examples → Firmata, and then click on StandardFirmata. This will open the Firmata setup code.</li>
<li>Click the Upload button in the Arduino IDE to upload the setup to your Arduino board.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/firmata-import-QepVjmhoyR-1200.avif 1200w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/firmata-import-QepVjmhoyR-1200.webp 1200w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Importing Standard Firmata setup sketch from examples in Arduino IDE" loading="lazy" decoding="async" src="https://flowfuse.com/img/firmata-import-QepVjmhoyR-1200.jpeg" width="1200" height="873" /></picture>
<em>Importing Standard Firmata setup sketch from examples in Arduino IDE</em></p>
<p>Once the upload is complete, the Arduino is ready to communicate via the Firmata protocol.</p>
<h3 id="step-3%3A-connecting-node-red-to-arduino-via-serial-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-3%3A-connecting-node-red-to-arduino-via-serial-communication"></a> Step 3: Connecting Node-RED to Arduino via Serial Communication</h3>
<p>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.</p>
<p>First, we will need to install a node that will enable communication between Node-RED and the Arduino via Firmata.</p>
<p><strong>Installing Arduino Node</strong></p>
<ol>
<li>Open the main menu by clicking the three horizontal lines in the top-right corner.</li>
<li>Click "Manage Palette" from the menu.</li>
<li>Switch to the "Install" tab and type "node-red-node-arduino" in the search field.</li>
<li>Click "Install" next to the node name.</li>
</ol>
<p>After installing the required node for Arduino, we will set up the flow in Node-RED to establish a connection with the Arduino.</p>
<p><strong>Establishing Connection with Arduino</strong></p>
<ol>
<li>Drag any Arduino node onto the canvas</li>
<li>Double-click the node to open its configuration window.</li>
<li>In the new window that opens, enter the port name for your Arduino (for example, COM5 on Windows or /dev/ttyUSB0 on Linux/macOS). You can find the correct port in the Arduino IDE or your system’s device manager.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/serial-port-config-Ndy1ZPkrKS-858.avif 858w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/serial-port-config-Ndy1ZPkrKS-858.webp 858w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Adding the port to the Arduino node" loading="lazy" decoding="async" src="https://flowfuse.com/img/serial-port-config-Ndy1ZPkrKS-858.jpeg" width="858" height="462" /></picture>
<em>Adding the port to the Arduino node</em></p>
<ol start="4">
<li>Click and deploy by clicking the top-right deploy button.</li>
</ol>
<p>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."</p>
<h3 id="step-4%3A-sending-commands-to-arduino" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-4%3A-sending-commands-to-arduino"></a> Step 4: Sending Commands to Arduino</h3>
<p>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.</p>
<ol>
<li>Drag the Arduino-out node onto the canvas.</li>
<li>Double-click the node to open its configuration window.</li>
<li>Select the correct configuration (serial port) that you set up in Step 3.</li>
<li>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/1).</li>
</ol>
<p>Based on the type of data you want to send, select the appropriate pin type:</p>
<ul>
<li>Digital (0/1) – accepts 0, 1</li>
<li>Analog (0-255) (PWM) – accepts Integer values from 0 to 255</li>
<li>Servo (0-180) – accepts Integer values from 0 to 180</li>
<li>String – to send a string to the Arduino</li>
</ul>
<ol start="5">
<li>Once you've configured the pin type, enter the pin number (e.g., 13 for the built-in LED).</li>
<li>Click Done.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/control-led-M0WmCUgxG2-852.avif 852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/control-led-M0WmCUgxG2-852.webp 852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring Arduino-out node" loading="lazy" decoding="async" src="https://flowfuse.com/img/control-led-M0WmCUgxG2-852.jpeg" width="852" height="660" /></picture><br />
<em>Configuring Arduino-out node</em></p>
<ol start="7">
<li>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).</li>
<li>Connect the output of each Inject node to the input of the Arduino-out node.</li>
<li>Deploy the flow.</li>
</ol>
<p>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.</p>
<p>Below is the flow that allows you to control the LED connected to pin 13.</p>
<div id="nr-flow-165" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow165 = "\n[{\"id\":\"fbe0eb6547cbfed7\",\"type\":\"arduino out\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"pin\":\"13\",\"state\":\"OUTPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":739.8345947265625,\"y\":645.8272094726562,\"wires\":[]},{\"id\":\"e222d2df8c62483d\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"Turn LED on\",\"props\":[{\"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\":[{\"p\":\"payload\"}],\"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\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow165.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-165') })</script>
<p>If you're interested in learning how to create a dashboard, you can refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started Guide</a>. It will help clarify basic dashboard concepts and guide you through building a simple dashboard interface.</p>
<h3 id="step-5%3A-receiving-inputs-from-the-arduino" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-5%3A-receiving-inputs-from-the-arduino"></a> Step 5: Receiving Inputs from the Arduino</h3>
<p>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.</p>
<ol>
<li>Drag the Arduino-in node onto the canvas.</li>
<li>Double-click the node to open its configuration window.</li>
<li>Select the serial port to which your Arduino is connected.</li>
<li>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.</li>
<li>Enter the pin number (e.g., pin 9, if your sensor is connected to pin 9 on the Arduino).</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/read-sensor-data-iOP7GF-aCn-856.avif 856w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/read-sensor-data-iOP7GF-aCn-856.webp 856w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring Arduino-in node" loading="lazy" decoding="async" src="https://flowfuse.com/img/read-sensor-data-iOP7GF-aCn-856.jpeg" width="856" height="704" /></picture><br />
<em>Configuring Arduino-in node</em></p>
<p><em>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).</em></p>
<ol start="6">
<li>Click Done.</li>
<li>Drag the Debug node onto the canvas and connect its input to the output of the Arduino-in node.</li>
<li>Deploy the flow.</li>
</ol>
<p>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.</p>
<p>Below, I have provided the flow that reads the IR object detection sensor data connected to pin 9, in case you need it.</p>
<div id="nr-flow-166" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow166 = "\n[{\"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\":[]},{\"id\":\"d7663aaf.47194\",\"type\":\"arduino-board\",\"device\":\"COM5\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow166.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-166') })</script>
<h3 id="step-6%3A-creating-an-automation-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#step-6%3A-creating-an-automation-flow"></a> Step 6: Creating an Automation Flow</h3>
<p>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.</p>
<p>The idea is to trigger an action, such as turning on an LED, when the sensor detects an object.</p>
<ol>
<li>Drag a Switch node onto the canvas.</li>
<li>Double-click on the Switch node to open its configuration window and add the following conditions:</li>
</ol>
<ul>
<li>msg.payload == 1</li>
<li>msg.payload == 0</li>
</ul>
<ol start="3">
<li>Click Done to save the configuration.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/switch-H2PC_3hrSV-854.avif 854w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/switch-H2PC_3hrSV-854.webp 854w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Adding conditions in the Switch node to check if the object is detected or not." loading="lazy" decoding="async" src="https://flowfuse.com/img/switch-H2PC_3hrSV-854.jpeg" width="854" height="646" /></picture>
<em>Adding conditions in the Switch node to check if the object is detected or not.</em></p>
<ol start="4">
<li>Connect the Switch node's input to the Arduino-in node's output.</li>
<li>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.</li>
<li>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.</li>
</ol>
<p>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).</p>
<p>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.</p>
<div id="nr-flow-167" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow167 = "\n[{\"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\":[{\"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\":[{\"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\":[{\"t\":\"eq\",\"v\":\"1\",\"vt\":\"num\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":330,\"y\":460,\"wires\":[[\"8f56a3d6ac64e87c\"],[\"49b04d8b6f015846\"]]},{\"id\":\"d6e251e983f908ac\",\"type\":\"arduino out\",\"z\":\"FFF0000000000001\",\"name\":\"Control LED\",\"pin\":\"13\",\"state\":\"OUTPUT\",\"arduino\":\"d7663aaf.47194\",\"x\":770,\"y\":460,\"wires\":[]},{\"id\":\"d7663aaf.47194\",\"type\":\"arduino-board\",\"device\":\"COM5\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow167.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-167') })</script>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/FTuxOy16nwo?si=i7wds6zH0Hpo0TTM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/interacting-with-arduino-using-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<p>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.</p>
<p>It’s a simple yet powerful way to interact with your Arduino, with endless possibilities. Enjoy exploring and building!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/FlowFuse 2.14: Announcing Third-Party Broker Integration, UNS Schemas, Enhanced Auth on Remote Instances and more!A huge wave of new features in FlowFuse elevates your MQTT experience as well as providing improved Remote Instances security and version control too2025-02-11T00:00:00ZJoe Pavitt<p>This release is full of highlights, which we’ll break down into two key areas - the <strong>FlowFuse MQTT Experience</strong> and <strong>Remote Instances</strong> - so you can better digest all of the great new functionality introduced in FlowFuse 2.14.</p>
<!--more-->
<h2 id="mqtt-experience-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#mqtt-experience-in-flowfuse"></a> MQTT Experience in FlowFuse</h2>
<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">October we announced the FlowFuse MQTT Broker</a>, included in our Starter, Team, and Enterprise tiers. It lets you setup your own secure clients to begin publishing and subscribing to your own topics, and building out your full event-driven applications.</p>
<p>We see the MQTT experience and Node-RED experience regularly paired together, and so it's one of our missions at FlowFuse to provide the best developer experience for building out your event-driven applications when these two elements are in play.</p>
<h3 id="third-party-broker-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#third-party-broker-integration"></a> Third Party Broker Integration</h3>
<p>Whilst the Team Broker is a great addition, we are aware that many of our customers already have their own existing MQTT brokers and infrastructure, so what does FlowFuse bring to the table for them?</p>
<p>With this new release, it is now possible for you to connect your external brokers to FlowFuse. With that, you'll get access to the same great features to help you gain a clear understanding of the activity on your broker, and the structure of the data and topics that are being used.</p>
<h3 id="schema-generation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#schema-generation"></a> Schema Generation</h3>
<p>A pain point we've seen for many customers is collaboration around a single UNS, or MQTT Broker. There is limited (if any) documentation, and to create that, especially at industrial scale, would be a monstrous and time-consuming task. With that in mind, FlowFuse now offers <strong>automated schema generation</strong>.</p>
<p>FlowFuse now generates a schema for your MQTT Broker's topic hierarchy, whether you're using the in-built FlowFuse Team Broker, or integrating your own, without you needing to do anything. The schema is generated using the industry-standard, open-sourced, <a href="https://www.asyncapi.com/">AsyncAPI</a>, and is clearly presented to you in the FlowFuse UI in two formats.</p>
<h4 id="topic-hierarchy-editor" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#topic-hierarchy-editor"></a> Topic Hierarchy Editor</h4>
<p>The first view lives within the FlowFuse UI that you're familiar with, will display your interactive topic hierarchy which you can explore, and is predominantly the "edit" and "review" view of your hierarchy.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-topic-hierarchy-5V0IbTEjsk-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-topic-hierarchy-5V0IbTEjsk-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Topic Hierarchy view for a given Broker in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-topic-hierarchy-5V0IbTEjsk-1920.jpeg" width="1920" height="1101" /></picture>
<em>Screenshot of the Topic Hierarchy view for a given Broker in FlowFuse</em></p>
<p>For each topic, you can define descriptions so that team members and anyone interacting with your broker can get a clear understanding of how to get the most out of the hierarchy and associated data.</p>
<p>We have also hopped the first piece of our Smart Suggestions, which is an agent that runs and works out the structure of the payloads to each of your topics. Right now, this is shown in the UI under "Detected Schema", but we have bigger plans here, that hopefully you'll be seeing more of in the very near future.</p>
<h4 id="personalized-uns-documentation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#personalized-uns-documentation"></a> Personalized UNS Documentation</h4>
<p>The second view is focussed on clarity, and collaboration.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-topic-docs-8AXpxTO6Dy-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-topic-docs-8AXpxTO6Dy-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the new "Schema Documentation" view provided for brokers on FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-topic-docs-8AXpxTO6Dy-1920.jpeg" width="1920" height="1112" /></picture>
<em>Screenshot of the new "Schema Documentation" view provided for brokers on FlowFuse</em></p>
<p>A big challenge with the Unified Namespace, and wider MQTT too, can be the lack of centralized structure and information around the topics and payloads that are being used. This can make it difficult to understand what is being published and subscribed to, and can make it hard to onboard new projects, team members or partners.</p>
<p>That is why we have built in a new "Schema Documentation" view, available for both the in-built FlowFuse Broker, and any third-party broker that you choose to connect.</p>
<p>This view is built from the automated topic hierarchy, manually added topic descriptions and will soon feature payload schema information from our upcoming Smart Schemas.</p>
<h2 id="remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#remote-instances"></a> Remote Instances</h2>
<p>This release sees two new major features coming to Remote Instances (previously known as "Devices").</p>
<h3 id="flowfuse-authentication-now-supported" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#flowfuse-authentication-now-supported"></a> FlowFuse Authentication now Supported</h3>
<p>You can now secure your Remote Instances with FlowFuse Authentication.</p>
<p>This means that you can now use the same credentials you use to log into FlowFuse, to authenticate any HTTP endpoints, such as the <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a>, on your Remote Instances. This also unlocks features like <a href="https://dashboard.flowfuse.com/user/multi-tenancy.html">Multi-Tenant Dashboards</a> that can run on your own hardware at the Edge.</p>
<p>Please note that this does require that your hardware has a connection to FlowFuse.</p>
<h3 id="version-history-for-remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#version-history-for-remote-instances"></a> Version History for Remote Instances</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-timeline-3m3ZBT69SK-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-timeline-3m3ZBT69SK-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the new "Version History" timeline view available for Remote Instances" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-timeline-3m3ZBT69SK-1920.jpeg" width="1920" height="1101" /></picture>
<em>Screenshot of the new "Version History" timeline view available for Remote Instances</em></p>
<p>This release brings another great update for Remote Instances: the "Timeline" view has been rolled out to provide you a clear picture of everything that has been running on your Remote Instance.</p>
<p>Here you will be able to see every time flows were deployed (for example from within <a href="https://flowfuse.com/docs/device-agent/quickstart/#developer-mode">Developer Mode</a>), when settings have updated, <a href="https://flowfuse.com/docs/user/snapshots/">Snapshots</a> have been created, or new flows have been deployed via a <a href="https://flowfuse.com/docs/user/devops-pipelines/">DevOps Pipeline</a>.</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#what-else-is-new%3F"></a> What Else Is New?</h2>
<p>For a full list of everything that went into our 2.13 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.14.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/flowfuse-release-2-14/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/Announcing Node-RED Academy!The new Node-RED Academy enables you to learn Node-RED, increase your expertise, and demonstrate your knowledge with shareable certificates.2025-02-05T00:00:00ZGreg Stoutenburg<p>The <a href="https://node-red-academy.learnworlds.com/">Node-RED Academy</a> 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.</p>
<!--more-->
<h2 id="what-is-node-red-academy%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/#what-is-node-red-academy%3F"></a> What is Node-RED Academy?</h2>
<p>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.</p>
<h2 id="can-i-earn-a-credential%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/#can-i-earn-a-credential%3F"></a> Can I Earn a Credential?</h2>
<p>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.</p>
<p>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.</p>
<h2 id="what-courses-are-included%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/#what-courses-are-included%3F"></a> What Courses are Included?</h2>
<p>Our first course, The <a href="https://node-red-academy.learnworlds.com/">Node-RED Fundamentals</a>, takes about 90 minutes to complete, is free and available now. <a href="https://node-red-academy.learnworlds.com/">Check it out!</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/academy-screenshot-OivjfDyQQQ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/academy-screenshot-OivjfDyQQQ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Node-RED Fundamentals course in progress" loading="lazy" decoding="async" src="https://flowfuse.com/img/academy-screenshot-OivjfDyQQQ-1920.jpeg" width="1920" height="1104" /></picture>
<em>Screenshot of the Node-RED Fundamentals course in progress</em></p>
<p>We also have plans for the following courses to follow in the future:</p>
<ul>
<li>Node-RED Advanced</li>
<li>Building Applications with Node-RED</li>
<li>Node-RED for Industry</li>
<li>Node-RED for Teams</li>
<li>FlowFuse Fundamentals</li>
<li>FlowFuse for Teams</li>
<li>FlowFuse Advanced</li>
</ul>
<p>This list will likely grow beyond this too. We will be evolving our courses based on feedback and ideas from the community too!</p>
<h2 id="who%E2%80%99s-behind-the-node-red-academy%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/#who%E2%80%99s-behind-the-node-red-academy%3F"></a> Who’s behind the Node-RED Academy?</h2>
<p>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.</p>
<p>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 <a href="https://app.flowfuse.com/account/create">here</a> to give it a go.</p>
<h2 id="how-do-i-get-started%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/02/node-red-academy-announcement/#how-do-i-get-started%3F"></a> How Do I Get Started?</h2>
<p>Head to <a href="https://node-red-academy.learnworlds.com/">Node-RED Academy</a> and sign up for free.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/academy-home-page-hLePyZrAy4-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/academy-home-page-hLePyZrAy4-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Node-RED Academy home page" loading="lazy" decoding="async" src="https://flowfuse.com/img/academy-home-page-hLePyZrAy4-1920.jpeg" width="1920" height="927" /></picture>
<em>Screenshot of the Node-RED Academy home page</em></p>
<div>
<p>The courses include everything you need to learn Node-RED, but you will also need access to an instance of Node-RED to follow along with exercises and apply your new knowledge. The easiest way to get access to Node-RED is with the FlowFuse Starter Plan.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Announcing%20Node-RED%20Academy%21">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/designing-topic-hierarchy-for-your-uns/Designing a Clear Topic Structure for Your UNSWhy Topic Structuring is Key to Scaling and Optimizing Your UNS2025-01-31T00:00:00ZSumit Shinde<p>Topic structuring is not just a technical concern when building a high-performance Unified Namespace (UNS) for manufacturing environments; it's a strategic design choice that can determine the system's scalability, efficiency, and overall effectiveness.</p>
<!--more-->
<p>A well-structured topic hierarchy is critical in manufacturing, where vast amounts of data flow from sensors, machines, and systems. By organizing your topics correctly, you can streamline data flow, simplify scaling, and make your system more manageable as your operations grow.</p>
<p>In this post, we'll explore the significance of topic structuring for your UNS, outline why it's essential for scalability and performance, and share best practices for designing a robust topic hierarchy that can evolve alongside your business.</p>
<h2 id="why-topic-structuring-is-crucial-for-your-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/designing-topic-hierarchy-for-your-uns/#why-topic-structuring-is-crucial-for-your-uns"></a> Why Topic Structuring is Crucial for Your UNS</h2>
<p>When building a UNS for manufacturing environments, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/">MQTT</a> is one of the most popular and preferred choices due to its lightweight, efficient, and scalable design. MQTT’s publish-subscribe model is perfect for handling the real-time, high-volume data flow in factories, where machines, sensors, and devices constantly generate information. However, while MQTT is a powerful tool, how you structure your topics plays a pivotal role in ensuring that your UNS is scalable and efficient.</p>
<p>Manufacturing operations often experience rapid growth. The sheer number of sensors, machines, and production lines increases over time, and so does the complexity of the data. MQTT topics are hierarchical, which means they follow a tree-like structure with levels separated by a forward slash (/). This hierarchical structure can mirror the physical layout of your factory floor, providing a logical, scalable framework for your data.</p>
<p>For example:</p>
<p><code>/factory/line1/machine1/temperature</code></p>
<p>This topic structure indicates that the data comes from machine1 on line1 of your factory, specifically from a temperature sensor. The structure is intuitive because it directly reflects the factory’s layout.
Adding more machines, sensors, or production lines is straightforward as your factory grows.</p>
<p>For instance, as your factory adds a second production line, you can add topics like:</p>
<ul>
<li><code>/factory/line2/machine1/temperature</code></li>
<li><code>/factory/line2/machine1/vibration</code></li>
<li><code>/factory/line2/machine2/temperature</code></li>
</ul>
<p>This hierarchical system scales seamlessly as you add more machines, sensors, and production lines without creating unnecessary complexity.</p>
<p>A well-structured topic hierarchy improves the performance of both the network and edge devices. When monitoring or data analysis systems subscribe to topics, they can choose the data they need. This means they are not overwhelmed by unnecessary traffic, which could otherwise strain network bandwidth and device processing power.</p>
<p>For example, imagine a maintenance team only needs to monitor the temperature of machines in line 1. With a clean topic structure, they can subscribe to:</p>
<ul>
<li>
<p><code>/factory/line1/machine1/temperature </code></p>
</li>
<li>
<p><code>/factory/line1/machine2/temperature</code></p>
</li>
</ul>
<p>By filtering the data, they avoid receiving irrelevant data, such as vibration readings from other machines or temperatures from machines on different production lines. This reduces network load, ensures more responsive performance, and prevents overloads on edge devices.</p>
<p>A well-organized topic structure makes maintenance and troubleshooting much more efficient. When equipment malfunctions or a sensor starts reporting erroneous data in a smart factory, the ability to pinpoint the issue quickly is crucial. With a hierarchical topic system, you can easily trace the problem to a specific machine, sensor, or production line.</p>
<p>For instance, imagine a temperature sensor on machine 3 in line 2 reporting abnormal values. A topic like:</p>
<ul>
<li><code>/factory/line2/machine3/temperature</code></li>
</ul>
<p>Immediately indicates the affected machine and production line. This clarity lets your team act quickly, reducing downtime and improving system reliability.</p>
<p>Without a clear topic structure, identifying and diagnosing problems can become time-consuming, leading to extended downtime and inefficiencies.</p>
<p>As new equipment, sensors, or production lines are added to the factory, a well-structured topic system helps onboard new engineers, technicians, and operators easily. A clear hierarchy provides a visual map of the system, making it easier for new users to understand the architecture and begin working with it quickly.</p>
<h2 id="designing-a-topic-structure-for-your-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/designing-topic-hierarchy-for-your-uns/#designing-a-topic-structure-for-your-uns"></a> Designing a Topic Structure for Your UNS</h2>
<p>Before you start collecting data in your UNS, it’s essential to design your topic structure. While it might seem like a small step, it’s the foundation of your system. Taking the time to plan will save you significant time and effort down the road. More importantly, it gives you a clear, high-level view of your entire factory, which is crucial for scaling effectively.</p>
<p>First, think about the key components of your factory. For example, you might have different plants or production lines in a manufacturing setting. There will be machines or devices within each production line that produce data. Then, you’ll have various data points coming from sensors on these machines, such as temperature, humidity, or pressure.</p>
<p>By organizing your topics around these components, you’re setting up a structure that’s easy to scale.</p>
<p>Next, remember that MQTT topics are hierarchical, so think of them like a tree. At the top of the tree, you’ll have the broadest categories (like plants or regions). As you go down, you’ll get more specific, with production lines, machines, and then individual data points like sensor readings. The key is to keep things logical so that you can locate the data you need quickly. This organization lets you promptly expand your system later by adding new plants, lines, or machines without disrupting the entire structure.</p>
<p>The concept of structuring topics logically and hierarchically draws from a well-known framework in manufacturing: ISA-95. ISA-95 is a standard that defines a hierarchical model for organizing and managing manufacturing systems. It divides operations from the company level to individual machines, providing a clear structure for system management.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.avif 1421w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.webp 1421w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="ISA-95 Equipment Hierarchy Model" loading="lazy" decoding="async" src="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.jpeg" width="1421" height="978" /></picture>
<em>ISA-95 Equipment Hierarchy Model</em></p>
<p>Here’s a brief breakdown of the ISA-95 levels and how they can be translated to MQTT topics:</p>
<ul>
<li><strong>Level 0 – Physical Devices and Control</strong></li>
</ul>
<p>This is where the physical data originates: sensors, actuators, and devices directly interacting with machinery and production lines. These are typically represented as devices in your MQTT topic structure.</p>
<p>Example Topics:</p>
<ul>
<li>
<p><code>/plantA/productionLine1/machineB/sensor/temperature</code></p>
</li>
<li>
<p><code>/plantA/productionLine1/machineB/sensor/pressure</code></p>
</li>
<li>
<p><code>/plantA/productionLine2/machineC/sensor/humidity</code></p>
</li>
</ul>
<p>At this level, you're dealing with specific machines and sensors. The topic name clearly defines the device type (e.g., "sensor") and the type of data it generates (e.g., "temperature"). This structure makes tracking sensor data easy for each machine or production line.</p>
<ul>
<li><strong>Level 1 – Control Devices and Systems</strong></li>
</ul>
<p>This level represents the control systems that operate the machinery and manage the data flow. These systems include PLCs, SCADA systems, or other control devices that manage real-time operations.</p>
<p>Example Topics:</p>
<ul>
<li>
<p><code>/plantA/productionLine1/machineB/PLC/status</code></p>
</li>
<li>
<p><code>/plantA/productionLine1/machineB/PLC/mode</code></p>
</li>
<li>
<p><code>/plantA/productionLine2/machineC/SCADA/alerts</code></p>
</li>
</ul>
<p>Topics at this level might focus on the status and control functions of the machines. Separating control systems like PLCs or SCADA ensures that operational data (e.g., machine modes or alerts) is distinct from raw sensor data. This approach ensures that each system component can be monitored and managed independently.</p>
<ul>
<li><strong>Level 2 – Monitoring and Supervisory Control</strong></li>
</ul>
<p>At this level, systems monitor and manage operations. They might include higher-level systems that oversee the production lines, collect data from multiple PLCs, and trigger alerts or analyses based on predefined criteria.</p>
<p>Example Topics:</p>
<ul>
<li>
<p><code>/plantA/productionLine1/supervisor/alerts</code></p>
</li>
<li>
<p><code>/plantA/productionLine1/supervisor/performance</code></p>
</li>
<li>
<p><code>/plantA/productionLine2/supervisor/utilization</code></p>
</li>
</ul>
<p>Here, you might aggregate data from several control devices (like PLCs) and provide higher-level insight into the overall system. For example, a "performance" topic could aggregate sensor data to monitor the efficiency of a production line, while "alerts" might be used for system-wide warnings.</p>
<ul>
<li><strong>Level 3 – Manufacturing Operations Management</strong></li>
</ul>
<p>This level encompasses managing the overall production process, such as scheduling, production orders, and resource management. This is often where MES (Manufacturing Execution Systems) comes into play.</p>
<p>Example Topics:</p>
<ul>
<li>
<p><code>/plantA/productionLine1/MES/productionOrder</code></p>
</li>
<li>
<p><code>/plantA/productionLine2/MES/scheduling</code></p>
</li>
<li>
<p><code>/plantB/productionLine1/MES/inventoryStatus</code></p>
</li>
</ul>
<p>The data becomes more abstract at this level as you deal with business logic, production orders, and scheduling systems. For example, the "productionOrder" topic could track orders for specific products, while "inventoryStatus" could provide data on material availability for each production line.</p>
<ul>
<li><strong>Level 4 – Enterprise Resource Planning (ERP)</strong></li>
</ul>
<p>The highest level in the ISA-95 hierarchy is focused on enterprise-wide resource planning, financials, and decision-making processes. ERP systems integrate with manufacturing systems to provide broader business insights.</p>
<p>Example Topics:</p>
<ul>
<li>
<p><code>/enterprise/ERP/inventory/overview</code></p>
</li>
<li>
<p><code>/enterprise/ERP/sales/orders</code></p>
</li>
<li>
<p><code>/enterprise/ERP/production/metrics</code></p>
</li>
</ul>
<p>At the ERP level, topics reflect cross-plant business data like inventory, order management, or performance metrics. These are less granular than lower levels and provide decision-makers with high-level insights into the health of the overall business.</p>
<h2 id="best-practices-for-managing-your-topic-structure" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/designing-topic-hierarchy-for-your-uns/#best-practices-for-managing-your-topic-structure"></a> Best Practices for Managing Your Topic Structure</h2>
<p>As your UNS scales, following some essential best practices will ensure your topic structure remains efficient, secure, and easy to manage.</p>
<ol>
<li>Maintain Clear Documentation:</li>
</ol>
<p>First and foremost, maintaining clear documentation is key. A well-documented topic hierarchy is a reference point for everyone involved in the system—from developers and engineers to system administrators. This documentation should outline the naming conventions, the purpose of each topic, and how new issues should be added. Without it, there's a risk of inconsistency creeping into your system, especially as new data streams and devices are introduced. An apparent, organized reference ensures your team can efficiently navigate and expand the system without confusion.</p>
<ol start="2">
<li>Ensuring Consistency in Naming and Structure:</li>
</ol>
<p>Using clear, descriptive names for each topic and sticking to a consistent naming pattern across your system is essential for long-term success. A well-defined naming convention ensures that everyone involved—whether developers, engineers, or system administrators—can easily understand the purpose of each topic. Navigating your UNS becomes intuitive when topics are consistently named, and troubleshooting issues is much easier.</p>
<p>For instance, avoid vague or overly generic names like <code>/sensor1/data</code>, which don’t offer much context. Instead, adopt more descriptive, hierarchical names that reflect the actual source and nature of the data, such as <code>/plantA/productionLine1/machineB/temperature</code>. A consistent structure not only enhances system readability but also ensures scalability.</p>
<ol start="3">
<li>Keep Topic Names Simple and Avoid Special Characters:</li>
</ol>
<p>While it’s essential to have descriptive topic names, they should also be simple and easy to use. Long topic names can make working with your UNS quickly and efficiently harder. Also, avoid using spaces or special characters, which might cause compatibility issues with some MQTT brokers or clients.</p>
<ol start="4">
<li>Perform Regular Topic Cleanup and Expiration:</li>
</ol>
<p>Next, don’t overlook the importance of topic cleanup and expiration. Over time, unused or obsolete topics can accumulate, adding unnecessary complexity and overhead to the system. Left unchecked, these stale topics can lead to unwanted confusion. It’s important to regularly audit the issues in your UNS, archiving or removing those no longer needed. While some MQTT systems support automatic topic expiration, implementing manual checks as part of your routine system maintenance is still a good practice. You’ll also want to manage the use of retained messages carefully. While they can help provide the latest state to new subscribers, overuse or misuse can lead to outdated information circulating across the system. Be mindful about which topics should retain data and ensure they are updated or cleaned up regularly.</p>
<ol start="5">
<li>Implement Robust Access Control:</li>
</ol>
<p>Access control is essential in managing a large-scale UNS. The hierarchical structure of MQTT topics naturally supports role-based access control (RBAC), allowing you to assign permissions based on topics. This ensures users and devices only access the data they need.</p>
<p>For example, engineers might only need access to machine-level sensor data, while plant managers require broader visibility into performance metrics across production lines. Additionally, you can restrict which devices can publish data on specific topics, ensuring only authorized systems send critical updates.</p>
<p>By defining clear access rules, you enhance security, maintain data integrity, and ensure that your UNS can scale efficiently as your organization grows.</p>
<h2 id="effortless-mqtt-and-topic-management-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/designing-topic-hierarchy-for-your-uns/#effortless-mqtt-and-topic-management-with-flowfuse"></a> Effortless MQTT and Topic Management with FlowFuse</h2>
<p>FlowFuse is more than just a low-code platform; it’s a game-changer for building and scaling your UNS. By seamlessly integrating MQTT, FlowFuse empowers you to connect, manage, and scale your industrial data systems efficiently.</p>
<p>FlowFuse's built-in MQTT broker gives you high-performance data handling without additional infrastructure overhead. The platform also features an interface for managing MQTT clients with access control for topics and an intuitive Topic Hierarchy View, which provides a real-time, visual representation of your entire MQTT topic structure. This makes organizing, monitoring, and managing your topics easy, ensuring clarity and consistency as your system grows.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.avif 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.webp 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing FlowFuse topic hierarchy interface for UNS" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.jpeg" width="1782" height="858" /></picture>
<em>Image showing FlowFuse topic hierarchy interface for UNS</em></p>
<p>What truly stands out is FlowFuse's ability to handle both legacy and modern industrial protocols, effortlessly bridging the gap between old and new systems. Whether adding new devices, integrating data streams, or scaling to thousands of devices, FlowFuse gives you the flexibility, scalability, and security you need to optimize your UNS at every step.</p>
<p>FlowFuse doesn’t just help you manage topics—it streamlines collaboration, enhances system performance, and accelerates your journey toward a fully integrated, future-proof UNS.</p>
<div>
<p>Looking to simplify your UNS management? Unlock the full potential of your data architecture with FlowFuse.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Designing%20a%20Clear%20Topic%20Structure%20for%20Your%20UNS">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/How to Choose the Right IIoT Device Management Software for Your BusinessKey Features and Considerations for Effective IIoT Device Management2025-01-24T00:00:00ZSumit Shinde<p>With more devices being connected across industrial environments, managing them can get pretty overwhelming. The right IIoT device management software can help you stay on top of things—keeping everything secure, up-to-date, and running smoothly. But with so many options out there, how do you figure out which one’s best for your business?</p>
<!--more-->
<p>This guide will take you through the key features and considerations to keep in mind when choosing IIoT device management software. Whether you’re just starting out with IIoT or looking to improve your current setup, we’ve got some helpful insights to point you in the right direction.</p>
<h3 id="1.-vendor-reputation-and-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#1.-vendor-reputation-and-support"></a> 1. Vendor Reputation and Support</h3>
<p>When it comes to IIoT systems, the right vendor can make all the difference. After all, your device management platform will be at the heart of your manufacturing operations, and any hiccups can result in costly downtime. That's why choosing a vendor with a solid reputation is non-negotiable. Start by researching companies that have a proven track record in industrial environments. Look for vendors who not only understand the technical side of IIoT but also know how to support real-world use cases in manufacturing. Customer reviews and case studies can provide valuable insight into their reliability, service quality, and expertise.</p>
<p>One of the most important factors to consider is the availability of customer support. Your platform should be backed by a responsive, accessible support team, especially when things go wrong. In high-pressure situations, like production delays or equipment failures, 24/7 support can be a game-changer. You don’t want to be waiting hours for help when every minute counts. Look for vendors that offer clear, well-documented resources along with robust, round-the-clock support to guide you through troubleshooting and problem resolution.</p>
<h3 id="2.-remote-access" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#2.-remote-access"></a> 2. Remote Access</h3>
<p>The best device management software should make it easy to manage devices from anywhere. Remote access is crucial, especially for businesses with devices spread across different locations or those that operate 24/7. With remote access, your team can monitor device performance, troubleshoot issues, and push updates without needing to be on-site. This flexibility reduces downtime and ensures everything stays operational, no matter where you are.</p>
<p>For example, imagine you have a factory in a remote area and a critical device experiences a malfunction. Without remote access, you would need to send a technician on-site, which could involve travel time, accommodation, and delays in getting the machine back up and running. This can be costly—not just in terms of money but also in lost production time. With remote access, however, your team can instantly diagnose the issue, push an update, or even resolve the problem without the need for a costly site visit. This means less downtime, faster resolutions, and a more efficient operation overall.</p>
<h3 id="3.-automated-monitoring-and-alerts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#3.-automated-monitoring-and-alerts"></a> 3. Automated Monitoring and Alerts</h3>
<p>In manufacturing, keeping a constant watch on device health is crucial to avoid unexpected failures that could lead to costly downtime. The ideal device management platform should automatically track key performance metrics—such as device crashes, CPU usage, and more. By continuously monitoring these metrics, the platform can quickly identify any abnormalities and alert your team before small issues escalate into major problems.</p>
<p>For example, imagine a device on the factory floor experiences high CPU usage and is about to crash. An automated alert would immediately notify your team, allowing them to take swift action and address the issue before it causes a disruption. This proactive approach helps keep production running smoothly and minimizes unnecessary downtime.</p>
<p>The platform should also allow you to set custom thresholds for specific performance metrics. This way, your team will only receive alerts for critical issues, reducing the noise from less important events. With this level of precision, responses become faster and more focused, ensuring that the most pressing concerns are addressed promptly.</p>
<p>Automated monitoring and alerts not only save time but also increase the reliability and productivity of your IIoT ecosystem. By catching potential failures early, you can prevent unplanned downtime and ensure that your operations remain efficient and uninterrupted.</p>
<h3 id="4.-device-grouping-and-scalability" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#4.-device-grouping-and-scalability"></a> 4. Device Grouping and Scalability</h3>
<p>As your IIoT network grows, managing each device individually can quickly become overwhelming. That's why it's essential to choose a platform that allows you to group devices by function, location, or type. For example, you could group all the devices in Production Line 1, making it easier to deploy workflows, software updates, or configurations with just a single click for all.</p>
<p>Device grouping simplifies the management process, enabling you to update settings, deploy updates, and troubleshoot multiple devices at once. Scalability is also crucial—your platform should seamlessly accommodate new devices as your operations expand, without adding unnecessary complexity or slowing down performance.</p>
<p>With the right platform, you can keep your IIoT network organized and scalable, ensuring that as your business grows, device management remains efficient and hassle-free.</p>
<h3 id="5.-wide-range-of-device-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#5.-wide-range-of-device-support"></a> 5. Wide Range of Device Support</h3>
<p>In a busy manufacturing environment, you're working with a mix of devices—some on Linux, some on Windows, and others with their own custom software. The last thing you want is to have to juggle multiple platforms to manage all your devices.</p>
<p>Find a device management system that works with all of them. A flexible platform will let you manage all your devices, regardless of their operating system, from one central dashboard. This makes your life a lot easier and keeps your operations running smoothly.</p>
<h3 id="6.-built-in-devops-toolset" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#6.-built-in-devops-toolset"></a> 6. Built-in DevOps Toolset</h3>
<p>Automation is essential for boosting manufacturing efficiency. device management platforms with built-in DevOps tools integrate easily with production systems. They automate tasks like device setup, configuration, and software updates, helping reduce downtime and speed up troubleshooting.</p>
<p>DevOps pipelines allow you to create workflows tailored to your needs, automating updates and maintenance for IIoT devices. This means your platform can push updates without interrupting production, reducing the need for manual work.</p>
<p>With the flexibility to create custom DevOps pipelines, you can manage devices more efficiently, save time, minimize errors, and keep everything aligned with production requirements.</p>
<h3 id="7.-real-time-collaboration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#7.-real-time-collaboration"></a> 7. Real-Time Collaboration</h3>
<p>Managing IIoT devices often involves teams working together, sometimes from different locations. That’s why having a platform that supports real-time collaboration is so important. It allows your team to stay in sync, making it easier to troubleshoot issues or implement updates without delays.</p>
<p>Look for a platform that lets multiple team members access and manage devices at the same time. This way, everyone can contribute to resolving problems or pushing updates without stepping on each other’s toes. Real-time collaboration helps keep things running smoothly, especially when quick responses are needed.</p>
<h3 id="8.-accidental-recovery-or-rollback" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#8.-accidental-recovery-or-rollback"></a> 8. Accidental Recovery or Rollback</h3>
<p>Another essential feature to look for is the ability to recover from system failures or accidental misconfigurations. Accidents can happen at any time, and being able to revert to a previous stable state quickly can save you from costly downtime and operational disruptions.</p>
<p>The ideal IIoT device management platform should include a snapshot or rollback functionality. This allows you to take snapshots of your device configurations and system state at various points in time. In the event of an issue, you can simply roll back to a previous snapshot, restoring your devices to their last known good state.</p>
<p>This feature is especially critical in production environments where system stability is key to preventing operations interruptions. It minimizes downtime, ensures data integrity, and provides peace of mind, knowing that you can recover quickly from mistakes or technical failures.</p>
<h3 id="9.-system-and-audit-log-management" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#9.-system-and-audit-log-management"></a> 9. System And Audit Log Management</h3>
<p>When it comes to managing IIoT devices, keeping track of what’s happening in your system is essential. A good platform will automatically log everything—device activities, errors, and user actions—so you can stay on top of things.</p>
<p>Audit logs are especially helpful because they tell you who accessed the system, what actions were taken, and when they happened. This level of visibility makes it easier to spot any security risks or unauthorized changes early, helping you address them before they turn into bigger issues.</p>
<p>These logs are also crucial for compliance. By maintaining a clear record of all changes, you can easily demonstrate that your system meets industry regulations and standards. This adds an extra layer of security and peace of mind, knowing that everything is being properly documented and monitored.</p>
<h3 id="10.-strong-security-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#10.-strong-security-features"></a> 10. Strong Security Features</h3>
<p>When it comes to IIoT, security is a top priority, especially with sensitive production data involved. Look for a platform that offers end-to-end encryption to ensure your data stays protected as it moves between devices and the cloud.</p>
<p>Access control is also important. A good IIoT platform will limit device access based on user roles, so only authorized personnel can view or make changes to sensitive data. This helps keep things secure by preventing unauthorized access.</p>
<p>To stay ahead of potential security risks, the platform should include vulnerability scanning tools like SBOM (Software Bill of Materials). These tools help you track and manage any risks from third-party software, giving you peace of mind.</p>
<p>For added protection, choose a platform that supports multi-factor authentication (MFA). MFA provides an extra layer of security in case login credentials are compromised.</p>
<p>Finally, make sure the platform secures device-to-cloud for remote accesss connections with methods like SSH tunneling and VPNs. These help ensure that data is transmitted safely and only accessible by trusted users, no matter where your devices are located.</p>
<h3 id="11.-user-friendly-interface" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#11.-user-friendly-interface"></a> 11. User-Friendly Interface</h3>
<p>Your team shouldn’t waste time struggling with a complicated platform in a busy manufacturing environment. Choose a device management platform with a simple, intuitive interface that makes monitoring devices, adjusting settings, and resolving issues quick and easy.</p>
<p>A clean, easy-to-navigate UI allows your team to stay focused on key tasks like tracking device health and performance without unnecessary complexity. Simple controls for configuring devices, applying updates, and onboarding new devices will save time and reduce frustration.</p>
<h3 id="12.-cost-considerations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#12.-cost-considerations"></a> 12. Cost Considerations</h3>
<p>When selecting a device management platform, it's crucial to look beyond just the initial cost. Consider the total cost of ownership, which includes licensing fees, ongoing maintenance, training, and technical support. These factors can add up over time, so it’s important to factor them into your decision-making process.</p>
<p>For example, a platform that charges based on the number of devices may seem affordable when you're just starting with a small network. However, as your operations scale, those costs can quickly increase. Take the time to compare different pricing models and choose the one that best fits your budget and future growth plans. This will ensure that your investment remains sustainable and supports your business over the long term.</p>
<h3 id="13.-integration-with-existing-systems" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#13.-integration-with-existing-systems"></a> 13. Integration with Existing Systems</h3>
<p>A good device management platform should make it easy for your devices to integrate with your existing systems—whether that’s your ERP, CRM, or maintenance software. This helps prevent data silos and ensures all your existing systems can work together and share data in real-time.</p>
<p>When your devices, machines, and software are connected, it keeps important information flowing freely across your operations. This makes it simpler to track inventory, monitor device health, or manage maintenance, and it helps you make more informed decisions.</p>
<p>The right platform should support a wide range of industrial protocols, from the latest to older systems, so everything can be included. By linking all your existing systems, you can create a single source of truth, keeping things running more smoothly and efficiently.</p>
<hr style="border: none; border-top: 3px solid rgba(173, 192, 252, 0.55); opacity: 0.3; margin-bottom: 20px;" />
<p>So, after all’s said and done, the secret to choosing the right IIoT device management software isn’t just about checking boxes on a list. It’s about finding a solution that keeps things simple, secure, and scalable for the long haul. Look for something that lets you stay in control, whether you're managing one device or a thousand, and make sure it’s backed by solid support when you need it most. In the end, the right platform should feel less like a tool and more like a partner that grows with you. Trust me, the right fit will make your life a whole lot easier.</p>
<h2 id="flowfuse-%E2%80%93-the-ultimate-iiot-device-management-solution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/how-to-choose-right-iot-device-management-tool/#flowfuse-%E2%80%93-the-ultimate-iiot-device-management-solution"></a> FlowFuse – The Ultimate IIoT Device Management Solution</h2>
<p>FlowFuse is an open-source industrial data platform that simplifies the management, scaling, and security of IIoT devices. Whether you're managing a handful of devices or overseeing thousands, FlowFuse <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/">consolidates everything into a single, intuitive interface</a>. With seamless cross-platform support, you can control all your devices from one central hub, streamlining operations and enhancing efficiency.</p>
<p>Built on the flexible <a href="https://flowfuse.com/node-red/">Node-RED</a> framework, FlowFuse easily integrates with a wide range of hardware, services, and APIs with over <a href="https://flowfuse.com/integrations/">5000 community contributed nodes</a>, allowing you to tailor and scale your IIoT network to meet evolving demands. The best part? You don’t need coding knowledge to get started—Node-RED’s visual programming interface makes it easy to create custom workflows with drag-and-drop functionality. With 24/7 expert support and an active community, you'll have access to the resources you need to resolve any challenges quickly.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/">Security is a top priority for FlowFuse</a>. With features like SSH tunneling for secure remote device access, end-to-end encryption, and multi-factor authentication, your data is protected no matter where it’s being transferred. Role-Based Access Control (RBAC) ensures that only authorized users can make critical changes, while SOC 2 compliance provides an added layer of assurance for your sensitive operations.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/">Real-time collaboration is built into the platform</a>, enabling your team to work together on projects, monitor devices remotely, and deploy updates simultaneously—all within a secure environment. <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">Role-Based Access Control (RBAC)</a> ensures that each team member has appropriate permissions based on their role. For example, admins can make critical changes, while operators can only monitor or update devices. This helps maintain control over sensitive functions while allowing your team to collaborate effectively. Together with RBAC, FlowFuse maximizes both security and productivity by ensuring the right people have access to the right tasks, at the right time.</p>
<p>If things go awry, FlowFuse’s <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/">snapshot</a> feature lets you quickly revert to a previous stable state, minimizing downtime and keeping your systems running smoothly. Advanced monitoring tools continuously track device performance, sending alerts if issues like crashes or resource overloads arise, so your team can act before problems escalate. Detailed device logs and audit trails make troubleshooting straightforward, helping you quickly pinpoint issues and maintain a secure environment.</p>
<p>FlowFuse offers <a href="https://flowfuse.com/pricing/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20pricing&utm_term=high_intent&utm_content=How%20to%20Choose%20the%20Right%20IIoT%20Device%20Management%20Software%20for%20Your%20Business/">flexible pricing plans</a>, including a free tier that allows you to manage up to two devices—so you can try the platform before making any commitment. Whether you’re just starting out or scaling your operations, FlowFuse provides the tools and support you need to efficiently manage your IIoT network.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=How%20to%20Choose%20the%20Right%20IIoT%20Device%20Management%20Software%20for%20Your%20Business">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/Why FlowFuse is the Complete Toolkit For Building UNS?The Open-Source Solution to Build and Manage a Successful UNS2025-01-20T00:00:00ZSumit Shinde<p>Unified Namespace (UNS) is changing the way data is managed in industrial environments. It’s becoming the key to more successful and productive operations. Many organizations have already implemented it, and others are still figuring out the best approach and platform to implement it. There are so many tools out there; how do you know which one is right for your UNS? It’s a big decision, and it can be overwhelming. The good news? FlowFuse is the toolkit you’ve been looking for! It’s an all-in-one platform to build your UNS—and it’s open-source!</p>
<!--more-->
<p>At its core, a Unified Namespace (<a href="https://flowfuse.com/solutions/uns/">UNS</a>) is a data architecture that centralizes all your data from devices, sensors, and systems into a single hub. It helps you make sense of everything by organizing, structuring, and standardizing your data for easy access and analysis. Instead of dealing with fragmented data silos, you get a unified, real-time view of your entire operation.</p>
<p>Think of it as the brain of your entire operation, connecting all your business events in one place. Whether you're tracking performance, optimizing workflows, or making real-time decisions, a well-designed UNS makes it all possible. For more details, check out our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/">Introduction to the Unified Namespace</a>.</p>
<h2 id="core-components-of-uns-and-how-flowfuse-fits-in" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#core-components-of-uns-and-how-flowfuse-fits-in"></a> <strong>Core Components of UNS and How FlowFuse Fits In</strong></h2>
<p>To truly understand why FlowFuse is the ultimate toolkit for building and managing UNS, we need to explore its core components and see how FlowFuse enhances each one to help create a successful Unified Namespace in industrial IoT environments.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/components-of-uns-FwOiTMy6lH-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/components-of-uns-FwOiTMy6lH-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Core Components of UNS" loading="lazy" decoding="async" src="https://flowfuse.com/img/components-of-uns-FwOiTMy6lH-1920.jpeg" width="1920" height="1385" /></picture><br />
<em>Core Components of UNS: Key Elements to Consider When Building Your UNS</em></p>
<h4 id="1.-connectivity-layer" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#1.-connectivity-layer"></a> <strong>1. Connectivity Layer</strong></h4>
<p>The Connectivity Layer is the foundation of your UNS ecosystem. It’s what collects data from all your devices and systems—whether that’s sensors on the factory floor, PLCs, or IoT devices—and sends it to your UNS. Without a strong connectivity layer, your UNS won’t have the data it needs to create a complete view of your operations.</p>
<p><strong>How FlowFuse Helps:</strong></p>
<p>This is where FlowFuse comes in. Built on the powerful Node-RED platform, FlowFuse takes the complexity out of connecting devices. Whether you're dealing with legacy systems (think Modbus or OPC-UA) or the latest IoT devices (like MQTT or HTTP), FlowFuse ensures that everything can speak the same language. It connects your old and new technologies, effortlessly streaming data into your UNS.</p>
<p>With more than 5,000 available community contributed nodes, FlowFuse helps bridge the gap between old and new technologies. For example, if you have machines that use Modbus and new sensors using MQTT, FlowFuse can help them all send data into your UNS without any issues.</p>
<h4 id="2.-data-transformation-layer" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#2.-data-transformation-layer"></a> <strong>2. Data Transformation Layer</strong></h4>
<p>Once you’ve connected your devices and started collecting data, it’s time to send that data to your UNS. However, before that happens, there's an important step: data transformation. The data you collect often comes in different formats, units, or structures, which can create confusion and inefficiency when trying to use it across your system.</p>
<p>This is where the Data Transformation Layer plays a key role. It’s responsible for standardizing and enriching the data, ensuring it’s consistent, accurate, and ready to be used by your entire IIoT system. Without this layer, your data would remain fragmented and inconsistent—making integration difficult and analysis unreliable. Without proper transformation, your UNS wouldn’t be a true UNS; it would just be an data repository or dump.</p>
<p><strong>How FlowFuse Fits In:</strong></p>
<p>FlowFuse simplifies data transformation with its intuitive Node-RED interface. This allows engineers to set up complex data processing workflows with minimal effort. Whether you need to convert units of measurement, clean raw data, or reformat it, FlowFuse offers a low-code environment where you can drag and drop nodes to handle these tasks without needing to write custom code.</p>
<p>Beyond transforming data formats, FlowFuse also enables data contextualization. As raw data flows in, it can be enriched with important metadata—such as timestamps, equipment IDs, or sensor locations—that add context and make the data more meaningful. This is vital for accurate analysis and informed decision-making.</p>
<p>For instance, imagine temperature readings coming from multiple devices, with some sensors reporting in Celsius, others in Fahrenheit, and others in Kelvin. FlowFuse can automatically standardize all these readings to a single unit (like Celsius) and add contextual information, such as which machine the data came from and its current operating status. This makes the data easy to understand and act upon in your UNS.</p>
<h4 id="3.-message-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#3.-message-broker"></a> <strong>3. Message Broker</strong></h4>
<p>In UNS), the Message Broker is the central hub where your data resides until it’s accessed or consumed by other systems. It ensures that data flows smoothly between devices and applications, using a publish-subscribe (pub-sub) model. Systems "subscribe" to topics and receive automatic updates whenever new data is published, keeping everything in sync and up to date.</p>
<p>The Message Broker must support the pub-sub model, which is a core requirement for the UNS. For more information on why this model is essential, please read our article on why UNS needs pub-sub. The pub-sub model decouples producers (publishers) from consumers (subscribers), meaning they don’t need to be directly connected or even aware of each other. This decoupling enhances flexibility and scalability. Additionally, it makes the UNS event-driven, eliminating the need for constant polling. Systems only receive data when it’s relevant, boosting efficiency and responsiveness.</p>
<p>MQTT is the ideal broker that supports the pub-sub model and popular choice for uns. It’s lightweight, efficient, and works well in environments with limited resources.</p>
<p><strong>How FlowFuse Fits In:</strong></p>
<p>With FlowFuse, you get a built-in MQTT Broker, which means there's no need to configure and maintain a separate system. This simplifies the connection of all your devices and systems, ensuring smooth data exchange within your UNS.
FlowFuse makes it easy to manage connections, organize topics, and configure security features such as TLS encryption and username/password authentication—all within a single platform. This keeps your setup streamlined and secure.</p>
<p>The MQTT Broker supports hierarchical topic structuring, allowing you to efficiently organize and manage your data flows. In FlowFuse, you have an interface to monitor all your UNS topics in a tree view, as well as a secure interface to manage your MQTT clients.</p>
<p>Node-RED in FlowFuse comes with standard MQTT nodes, making it easy to set up secure connections to your broker. You can quickly configure security features like TLS encryption and username/password authentication or dynamically subscribe to topics</p>
<hr style="border: none; border-top: 3px solid rgba(173, 192, 252, 0.55); opacity: 0.3; margin-bottom: 20px;" />
<p>FlowFuse makes building and managing a Unified Namespace (UNS) easy. It connects devices, transforms data into a usable format, and ensures smooth communication using an MQTT broker. Powered by Node-RED, it works with old and new systems, helping you scale and adapt quickly.</p>
<h2 id="what-makes-flowfuse-stand-out-in-industrial-iot%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#what-makes-flowfuse-stand-out-in-industrial-iot%3F"></a> <strong>What Makes FlowFuse Stand Out in Industrial IoT?</strong></h2>
<p>FlowFuse isn’t just another tool—it's a complete solution for building and managing a <strong>Unified Namespace (UNS)</strong>. While other platforms may specialize in certain areas, FlowFuse brings everything together: connectivity, data transformation, and message brokering, all in one platform.</p>
<p>Many tools excel in one area but fall short in others. Some may connect devices well but struggle with legacy systems or data transformation. FlowFuse solves these challenges by offering an all-in-one solution that works seamlessly with both modern and legacy systems.</p>
<p>As an open-source platform, FlowFuse removes the worry of vendor lock-in. You have the flexibility to transition to other services if your needs evolve, without worrying about compatibility issues.</p>
<p>FlowFuse also includes powerful features tailored for industrial environments. One standout is <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/">real-time collaboration</a>, which allows multiple engineers to work on <strong>Node-RED flows</strong> simultaneously—speeding up development and deployment. Additionally, you can <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/">remotely manage edge devices</a>, reducing the need for costly on-site visits for troubleshooting or updates.</p>
<p>When it comes to scaling, FlowFuse is built to grow with you. It supports horizontal scaling to balance workloads across multiple instances and vertical scaling to add more resources as your needs increase. Whether you’re scaling up or dealing with high workloads, FlowFuse ensures your infrastructure remains stable and efficient.</p>
<p>Security is a major priority with FlowFuse. It includes features like <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">role-based access control</a>, encryption, multi-factory authentication, and detailed <a href="https://flowfuse.com/docs/user/logs/#audit-log">audit logs</a> to keep your data secure and meet industry standards.</p>
<p>On top of that, FlowFuse provides features like <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/">DevOps pipeline management</a>, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/">snapshots for disaster recovery</a>, and much more, ensuring your systems are always running smoothly and reliably.</p>
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/why-flowfuse-is-complete-toolkit-for-uns/#summary"></a> <strong>Summary</strong></h2>
<p>FlowFuse is a complete platform for building and managing a Unified Namespace. It combines everything you need—connectivity, data transformation, and message brokering—into one easy-to-use solution. Open-source flexibility makes it simple to connect devices, scale your system, and keep data secure.</p>
<div>
<p>Want to learn more about how FlowFuse can help?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=Why%20FlowFuse%20is%20the%20Complete%20Toolkit%20For%20Building%20UNS%3F">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/Getting Started: Integrating Siemens S7 PLCs with Node-REDA Step-by-Step Beginner's Guide to Connect, Control, and Monitor Siemens S7 PLCs Using Node-RED2025-01-17T00:00:00ZSumit ShindeSteve McLaughlin<p>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.</p>
<!--more-->
<p>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.</p>
<p>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/IP). 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.</p>
<h2 id="prerequisite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#prerequisite"></a> Prerequisite</h2>
<p>Before integrating your Siemens S7 PLC with Node-RED, make sure you have the following :</p>
<ol>
<li>Before downloading the ladder program and all configurations and settings to your PLC, make sure you have the following settings:</li>
</ol>
<ul>
<li>Allow PUT/GET Communication from remote partners.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/allow-put-get-communication-L5V2UdnHtM-1212.avif 1212w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/allow-put-get-communication-L5V2UdnHtM-1212.webp 1212w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="PUT/GET Communication from remote partners is Allowed" loading="lazy" decoding="async" src="https://flowfuse.com/img/allow-put-get-communication-L5V2UdnHtM-1212.jpeg" width="1212" height="688" /></picture>
<em>PUT/GET Communication from remote partners is Allowed</em></p>
<ul>
<li>Provide full access to the PLC (no protection), allowing unrestricted access to data exchange.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/providing-full-access-to-plc-Zs4TMs1lxc-1209.avif 1209w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/providing-full-access-to-plc-Zs4TMs1lxc-1209.webp 1209w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Providing complete access to the PLC" loading="lazy" decoding="async" src="https://flowfuse.com/img/providing-full-access-to-plc-Zs4TMs1lxc-1209.jpeg" width="1209" height="624" /></picture>
<em>Providing complete access to the PLC</em></p>
<ol start="2">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/optimized-block-access-epLllY_7mP-1152.avif 1152w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/optimized-block-access-epLllY_7mP-1152.webp 1152w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Untick 'Optimized Block Access'." loading="lazy" decoding="async" src="https://flowfuse.com/img/optimized-block-access-epLllY_7mP-1152.jpeg" width="1152" height="382" /></picture>
<em>Untick 'Optimized Block Access.'</em></p>
<ol start="3">
<li>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 <a href="https://flowfuse.com/product/device-agent/">FlowFuse Device Agent</a> to install Node-RED on your device.</li>
</ol>
<ul>
<li>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. <a href="https://app.flowfuse.com/account/create">Sign up for free</a> to get started.</li>
</ul>
<ol start="4">
<li>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).</li>
</ol>
<h2 id="integrating-siemens-s7-plcs-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#integrating-siemens-s7-plcs-with-node-red"></a> Integrating Siemens S7 PLCs with Node-RED</h2>
<p>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/tower 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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ladder-to-control-lights-GaNHeuMTdK-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ladder-to-control-lights-GaNHeuMTdK-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Ladder Logic to Control Outputs for Managing Lights" loading="lazy" decoding="async" src="https://flowfuse.com/img/ladder-to-control-lights-GaNHeuMTdK-650.jpeg" width="650" height="325" /></picture><em>Ladder Logic to Control Outputs for Managing Lights</em></p>
<p>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.</p>
<h3 id="installing-the-s7-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#installing-the-s7-node"></a> Installing the S7 Node</h3>
<p>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 <code>node-red-contrib-s7</code>, which is quite popular. If this particular node is not suitable for your workflow you can find alternatives in the <a href="https://flows.nodered.org/search?term=siemens&type=node">Node-RED catalog</a>.</p>
<h4 id="steps-to-install-the-s7-node%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#steps-to-install-the-s7-node%3A"></a> Steps to Install the S7 Node:</h4>
<ol>
<li>Open your Node-RED editor in a web browser.</li>
<li>Open the main menu by clicking the three horizontal lines in the top-right corner.</li>
<li>Click "Manage Palette" from the menu.</li>
<li>Switch to the "Install" tab and type <code>node-red-contrib-s7</code> in the search field.</li>
<li>Click "Install" next to the node name.</li>
</ol>
<p>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.</p>
<h3 id="addressing-scheme-for-variables-in-node-red-with-the-s7-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#addressing-scheme-for-variables-in-node-red-with-the-s7-node"></a> Addressing Scheme for Variables in Node-RED with the S7 Node</h3>
<p>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.</p>
<table>
<thead>
<tr>
<th><strong>Node-RED Address</strong></th>
<th><strong>Step7 Equivalent</strong></th>
<th><strong>Data Type</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>DB5,X0.1</code></td>
<td><code>DB5.DBX0.1</code></td>
<td>Boolean</td>
<td>Bit 1 of byte 0 in DB5</td>
</tr>
<tr>
<td><code>DB23,BYTE1</code></td>
<td><code>DB23.DBB1</code></td>
<td>Number (Byte)</td>
<td>Byte 1 (0-255) of DB23</td>
</tr>
<tr>
<td><code>DB100,CHAR2</code></td>
<td><code>DB100.DBB2</code></td>
<td>String</td>
<td>Byte 2 of DB100 as Char</td>
</tr>
<tr>
<td><code>DB42,INT3</code></td>
<td><code>DB42.DBW3</code></td>
<td>Number (16-bit)</td>
<td>Signed 16-bit number at byte 3 in DB42</td>
</tr>
<tr>
<td><code>DB57,WORD4</code></td>
<td><code>DB57.DBW4</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number at byte 4 in DB57</td>
</tr>
<tr>
<td><code>DB13,DINT5</code></td>
<td><code>DB13.DBD5</code></td>
<td>Number (32-bit)</td>
<td>Signed 32-bit number at byte 5 in DB13</td>
</tr>
<tr>
<td><code>DB19,DWORD6</code></td>
<td><code>DB19.DBD6</code></td>
<td>Number (32-bit)</td>
<td>Unsigned 32-bit number at byte 6 in DB19</td>
</tr>
<tr>
<td><code>DB21,REAL7</code></td>
<td><code>DB21.DBD7</code></td>
<td>Floating Point (32)</td>
<td>Floating point number at byte 7 in DB21</td>
</tr>
<tr>
<td><code>DB2,S7.10*</code></td>
<td>-</td>
<td>String</td>
<td>String (length 10) starting at byte 7 in DB2</td>
</tr>
<tr>
<td><code>I1.0</code></td>
<td><code>I1.0</code></td>
<td>Boolean</td>
<td>Bit 0 of byte 1 in input area</td>
</tr>
<tr>
<td><code>Q2.1</code></td>
<td><code>Q2.1</code></td>
<td>Boolean</td>
<td>Bit 1 of byte 2 in output area</td>
</tr>
<tr>
<td><code>M3.2</code></td>
<td><code>M3.2</code></td>
<td>Boolean</td>
<td>Bit 2 of byte 3 in memory area</td>
</tr>
<tr>
<td><code>IB4</code></td>
<td><code>IB4</code></td>
<td>Number (Byte)</td>
<td>Byte 4 (0-255) in input area</td>
</tr>
<tr>
<td><code>QB5</code></td>
<td><code>QB5</code></td>
<td>Number (Byte)</td>
<td>Byte 5 (0-255) in output area</td>
</tr>
<tr>
<td><code>MB6</code></td>
<td><code>MB6</code></td>
<td>Number (Byte)</td>
<td>Byte 6 (0-255) in memory area</td>
</tr>
<tr>
<td><code>IC7</code></td>
<td><code>IB7</code></td>
<td>String</td>
<td>Byte 7 of input area as Char</td>
</tr>
<tr>
<td><code>QC8</code></td>
<td><code>QB8</code></td>
<td>String</td>
<td>Byte 8 of output area as Char</td>
</tr>
<tr>
<td><code>MC9</code></td>
<td><code>MB9</code></td>
<td>String</td>
<td>Byte 9 of memory area as Char</td>
</tr>
<tr>
<td><code>II10</code></td>
<td><code>IW10</code></td>
<td>Number (16-bit)</td>
<td>Signed 16-bit number at byte 10 in input area</td>
</tr>
<tr>
<td><code>QI12</code></td>
<td><code>QW12</code></td>
<td>Number (16-bit)</td>
<td>Signed 16-bit number at byte 12 in output area</td>
</tr>
<tr>
<td><code>MI14</code></td>
<td><code>MW14</code></td>
<td>Number (16-bit)</td>
<td>Signed 16-bit number at byte 14 in memory area</td>
</tr>
<tr>
<td><code>IW16</code></td>
<td><code>IW16</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number at byte 16 in input area</td>
</tr>
<tr>
<td><code>QW18</code></td>
<td><code>QW18</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number at byte 18 in output area</td>
</tr>
<tr>
<td><code>MW20</code></td>
<td><code>MW20</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number at byte 20 in memory area</td>
</tr>
<tr>
<td><code>IDI22</code></td>
<td><code>ID22</code></td>
<td>Number (32-bit)</td>
<td>Signed 32-bit number at byte 22 in input area</td>
</tr>
<tr>
<td><code>QDI24</code></td>
<td><code>QD24</code></td>
<td>Number (32-bit)</td>
<td>Signed 32-bit number at byte 24 in output area</td>
</tr>
<tr>
<td><code>MDI26</code></td>
<td><code>MD26</code></td>
<td>Number (32-bit)</td>
<td>Signed 32-bit number at byte 26 in memory area</td>
</tr>
<tr>
<td><code>ID28</code></td>
<td><code>ID28</code></td>
<td>Number (32-bit)</td>
<td>Unsigned 32-bit number at byte 28 in input area</td>
</tr>
<tr>
<td><code>QD30</code></td>
<td><code>QD30</code></td>
<td>Number (32-bit)</td>
<td>Unsigned 32-bit number at byte 30 in output area</td>
</tr>
<tr>
<td><code>MD32</code></td>
<td><code>MD32</code></td>
<td>Number (32-bit)</td>
<td>Unsigned 32-bit number at byte 32 in memory area</td>
</tr>
<tr>
<td><code>IR34</code></td>
<td><code>IR34</code></td>
<td>Floating Point</td>
<td>Floating point number at byte 34 in input area</td>
</tr>
<tr>
<td><code>QR36</code></td>
<td><code>QR36</code></td>
<td>Floating Point</td>
<td>Floating point number at byte 36 in output area</td>
</tr>
<tr>
<td><code>MR38</code></td>
<td><code>MR38</code></td>
<td>Floating Point</td>
<td>Floating point number at byte 38 in memory area</td>
</tr>
<tr>
<td><code>DB1,DT0</code></td>
<td>-</td>
<td>Date</td>
<td>Timestamp in DATE_AND_TIME format</td>
</tr>
<tr>
<td><code>DB1,DTZ10</code></td>
<td>-</td>
<td>Date</td>
<td>Timestamp in DATE_AND_TIME format (UTC)</td>
</tr>
<tr>
<td><code>DB2,DTL2</code></td>
<td>-</td>
<td>Date</td>
<td>Timestamp in DTL format</td>
</tr>
<tr>
<td><code>DB2,DTLZ12</code></td>
<td>-</td>
<td>Date</td>
<td>Timestamp in DTL format (UTC)</td>
</tr>
<tr>
<td><code>DB57,RWORD4</code></td>
<td><code>DB57.DBW4</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number, Little-Endian at byte 4</td>
</tr>
<tr>
<td><code>DB13,RDI5</code></td>
<td><code>DB13.DBD5</code></td>
<td>Number (32-bit)</td>
<td>Signed 32-bit number, Little-Endian at byte 5</td>
</tr>
<tr>
<td><code>MRW20</code></td>
<td><code>MW20</code></td>
<td>Number (16-bit)</td>
<td>Unsigned 16-bit number, Little-Endian at byte 20</td>
</tr>
</tbody>
</table>
<p>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.</p>
<p>If you wanted integrate Siemens LOGO, please refer to the node's <a href="https://flows.nodered.org/node/node-red-contrib-s7">README</a>, as the addressing differs.</p>
<h3 id="configuring-the-s7-node-to-connect-to-the-plc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#configuring-the-s7-node-to-connect-to-the-plc"></a> Configuring the S7 Node to Connect to the PLC</h3>
<p>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</p>
<ol>
<li>Drag the S7 node onto the Node-RED canvas.</li>
<li>Double-click on the S7 node and click on the "+" icon to add a PLC configuration.</li>
<li>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.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/showing-rack-and-slot-fSW00wInXh-1226.avif 1226w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/showing-rack-and-slot-fSW00wInXh-1226.webp 1226w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing window from where you will get the Rack No and Slot No" loading="lazy" decoding="async" src="https://flowfuse.com/img/showing-rack-and-slot-fSW00wInXh-1226.jpeg" width="1226" height="596" /></picture>
<em>Image showing window from where you will get the Rack No and Slot No</em></p>
<ol start="5">
<li>Enter the Cycle Time (interval for communication with the PLC) and Timeout Duration (maximum time to wait for a response).</li>
<li>Once done, switch to the Variables tab and add all the variables with the correct address and name you want to read or write.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/s7-config-variables-8j3SIWYA0A-715.avif 715w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/s7-config-variables-8j3SIWYA0A-715.webp 715w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Adding Variables into s7 node" loading="lazy" decoding="async" src="https://flowfuse.com/img/s7-config-variables-8j3SIWYA0A-715.jpeg" width="715" height="609" /></picture>
<em>Adding Variables into s7 node</em></p>
<ol start="7">
<li>After adding the variables, click Add and then Done.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/s7-connection-configuration-bTEIqKDvWg-480.avif 480w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/s7-connection-configuration-bTEIqKDvWg-480.webp 480w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring S7 node for connection" loading="lazy" decoding="async" src="https://flowfuse.com/img/s7-connection-configuration-bTEIqKDvWg-480.jpeg" width="480" height="431" /></picture>
<em>Configuring S7 node for connection</em></p>
<h3 id="writing-data-to-the-plc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#writing-data-to-the-plc"></a> Writing Data to the PLC</h3>
<p>Now that you’ve configured the connection, it’s time to use Node-RED to write data to the PLC to control light.</p>
<ol>
<li>Drag the s7-out node onto the canvas.</li>
<li>Double-click on the node and select the variable to which you want to update or write a value.</li>
<li>Select the PLC configuration that we have added.</li>
<li>Click Done.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/configuring-s7-out-node-VEdjr7zwgv-374.avif 374w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/configuring-s7-out-node-VEdjr7zwgv-374.webp 374w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring S7-out Node to write data to plc" loading="lazy" decoding="async" src="https://flowfuse.com/img/configuring-s7-out-node-VEdjr7zwgv-374.jpeg" width="374" height="264" /></picture>
<em>Configuring S7-out Node to write data to plc</em></p>
<ol start="5">
<li>
<p>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 <a href="https://flowfuse.com/product/dashboard/">FlowFuse Dashboard</a> for easier interaction.</p>
</li>
<li>
<p>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.</p>
</li>
</ol>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/AilWMNPzP1Q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>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.</p>
<p>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: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/">Exploring FlowFuse Project Nodes</a>.</p>
<h3 id="reading-data-from-the-plc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#reading-data-from-the-plc"></a> Reading Data from the PLC</h3>
<p>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.</p>
<p>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: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/">Modernize Your Legacy Industrial Data - Part 2</a>.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/custom-function-storing-bits-in-word-M4hxLhWsP5-642.avif 642w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/custom-function-storing-bits-in-word-M4hxLhWsP5-642.webp 642w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Ladder diagram showing a custom function that stores the status of outputs in a single word within the PLC." loading="lazy" decoding="async" src="https://flowfuse.com/img/custom-function-storing-bits-in-word-M4hxLhWsP5-642.jpeg" width="642" height="226" /></picture>
<em>Custom ladder diagram function storing output statuses in a single word for optimized data retrieval.</em></p>
<p>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.</p>
<p>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.</p>
<p>Now, let’s begin reading the data from the PLC.</p>
<ol>
<li>Drag the <code>s7-in</code> node onto the canvas.</li>
<li>Double-click on the node to open the configuration and select the appropriate PLC configuration from the list of available connections.</li>
<li>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.</li>
<li>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 <code>DB.DBW2</code>, the format in the s7-in node will be <code>DB,WORD2</code>.</li>
<li>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.</li>
<li>Once your configuration is set, click "Done" and then deploy the flow to start reading data from the PLC.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/configuring-s7-in-node--fgOl70FEZ-372.avif 372w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/configuring-s7-in-node--fgOl70FEZ-372.webp 372w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring S7-in Node to Read data from plc" loading="lazy" decoding="async" src="https://flowfuse.com/img/configuring-s7-in-node--fgOl70FEZ-372.jpeg" width="372" height="324" /></picture>
<em>Configuring S7-in Node to Read data from plc</em></p>
<p>You can add a "Debug" node to the <code>s7-in</code> node's output to verify that the data is being read correctly.</p>
<p>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.</p>
<p>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.</p>
<p>The video below shows the updated dashboard interface used to monitor the stack light LED status:</p>
<iframe width="100%" height="315" src="https://www.youtube.com/embed/Nlyk_BATKGE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p>Here is the flow you can import into your FlowFuse remote instance and deploy. Ensure that you have installed <code>node-red-contrib-s7</code> and <code>node-red-contrib-buffer-parser</code>. 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.</p>
<div id="nr-flow-163" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow163 = "\n[{\"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\":[\"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\":[]},{\"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\":[]},{\"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\":[]},{\"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\":[{\"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\":[\"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\":[]},{\"id\":\"8d9a9dc4183a778e\",\"type\":\"buffer-parser\",\"z\":\"FFF0000000000001\",\"g\":\"23fd40630dbef712\",\"name\":\"\",\"data\":\"payload\",\"dataType\":\"msg\",\"specification\":\"spec\",\"specificationType\":\"ui\",\"items\":[{\"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\":[{\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow163.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-163') })</script>
<p>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 <code>@flowfuse/node-red-dashboard</code> and <code>@flowfuse/node-red-dashboard-2-ui-led</code>, and ensure the hosted instance is in the same FlowFuse team as your remote instance.</p>
<div id="nr-flow-164" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow164 = "\n[{\"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\":[\"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\":[]},{\"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\":[]},{\"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\":[]},{\"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\":\"/page1\",\"icon\":\"home\",\"layout\":\"notebook\",\"theme\":\"f6f5e7ae33bf6878\",\"breakpoints\":[{\"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\":\"02c25e8a30f9379d\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\":[\"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\":[{\"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\":[]},{\"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\":[{\"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\":[]},{\"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\":[{\"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\":[]},{\"id\":\"fb8801a4accc3c15\",\"type\":\"change\",\"z\":\"eb351e503901d04f\",\"g\":\"82cc6997fddd0b4b\",\"name\":\"\",\"rules\":[{\"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\":[{\"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\":[{\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow164.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-164') })</script>
<h2 id="troubleshooting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#troubleshooting"></a> Troubleshooting</h2>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/error-hiyPH47Hnb-516.avif 516w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/error-hiyPH47Hnb-516.webp 516w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt=""Error: This service is not implemented on the modeul or frame error was reported"" loading="lazy" decoding="async" src="https://flowfuse.com/img/error-hiyPH47Hnb-516.jpeg" width="516" height="168" /></picture>
<em>"Error: This service is not implemented on the modeul or frame error was reported"</em></p>
<p>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.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/integrating-siemens-s7-plcs-with-node-red-guide/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=Getting%20Started%3A%20Integrating%20Siemens%20S7%20PLCs%20with%20Node-RED">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/FlowFuse 2.13: Remote Instances, UNS Schemas & Improved Management at ScaleFlowFuse 2.13 brings clarity to "Instances" in FlowFuse, automated documentation for your MQTT Broker, better management and deployment to multiple Remote Instances, and more.2025-01-16T00:00:00ZJoe Pavitt<p>Happy New Year everyone! We're back with another release of FlowFuse, and whilst it's been a shorter sprint for us this time round, with most of the team out for a well-earned break over Christmas and New Year, that hasn't stopped us packing in lot of great new value into FlowFuse nonetheless.</p>
<!--more-->
<h2 id="hosted-%26-remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#hosted-%26-remote-instances"></a> Hosted & Remote Instances</h2>
<p>This release you'll notice that we've changed the terminology of two of our key concepts in FlowFuse:</p>
<ul>
<li><strong>Instances</strong> are now referred to as <strong>Hosted Instances</strong></li>
<li><strong>Devices</strong> are now referred to <strong>Remote Instances</strong></li>
</ul>
<p>We were finding that customers and prospects were getting confused over the term "Device", when in fact, it is a running <em>Instance</em> of Node-RED. You can have many Remote Instances running on the same piece of hardware, all using the FlowFuse Device Agent, and the term, "Device" made that misleading.</p>
<p>Remote Instances are just instances of Node-RED, deployed onto your own hardware and managed via FlowFuse and the FlowFuse Device Agent. We hope this change will make it clearer for everyone, and wanted to include this graphic which shows the updated terminology in the hierarchy of FlowFuse:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-hierarchy-70x4srxEoZ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-hierarchy-70x4srxEoZ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Hierarchy" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-hierarchy-70x4srxEoZ-1920.jpeg" width="1920" height="778" /></picture>
<em>Diagram to show the hierarchy of different concepts in FlowFuse</em></p>
<h2 id="documenting-your-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#documenting-your-uns"></a> Documenting Your UNS</h2>
<p>Just under three months ago, we <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/">released our own MQTT Service</a>, making it easy for your to configure MQTT clients through FlowFuse, and connect your hardware and applications. With the Unified Namespace (UNS) growing in popularity in Industry, we wanted to make sure that if you're using MQTT to run your UNS, then FlowFuse offers the best experience for managing it.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-topic-schema-Whyswviv-i-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-topic-schema-Whyswviv-i-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Topic Hierarchy Schema" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-topic-schema-Whyswviv-i-1920.jpeg" width="1920" height="1064" /></picture></p>
<p>With this in mind, we're now generating a formal schema for your MQTT Broker's topic hierarchy automatically, and we've made it possible to access the underlying schema through the FlowFuse interface. The schema we generate is using the industry-standard, open-sourced, <a href="https://www.asyncapi.com/">AsyncAPI</a>.</p>
<p>If you're interested in learning more about UNS, then we recommend taking a look at some of the following resources:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/">Introduction to the Unified Namespace</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/">Why you need a Unified Namespace</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">Building a Unified Namespace (UNS) with FlowFuse</a></li>
<li><a href="https://flowfuse.com/whitepaper/uns-decoupling-data-producers-and-consumers/"><strong>Free whitepaper:</strong> UNS - Decoupling data producers and consumers</a></li>
</ul>
<h3 id="future-plans" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#future-plans"></a> Future Plans</h3>
<p>This is very much a first iteration and we have work underway to improve it. Right now, the generated spec is pretty minimal - there is lots of scope to add more information and present it in a clearer and more interactive format. This will make it easy for your whole development team, and anyone else that needs to know, to get a clear picture of the topics and payloads that are being used in your MQTT Broker.</p>
<p>We're also looking at how this information can enhance the development experience within Node-RED itself.</p>
<h2 id="managing-groups-of-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#managing-groups-of-instances"></a> Managing Groups of Instances</h2>
<p>We've introduced a new view in FlowFuse which you can find in the "Operations" section of the side navigation. This new view is called "Groups" and it allows you to group your Remote Instances together, making it easier to manage and deploy to multiple instances at once.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-team-groups-9hVBJ2ZZfq-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-team-groups-9hVBJ2ZZfq-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Groups view" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-team-groups-9hVBJ2ZZfq-1920.jpeg" width="1920" height="1254" /></picture></p>
<p>This functionality has been in FlowFuse for a little while, but was buried down in the "Applications" view, and found users were missing it. We've now brought it to the forefront, and made it easier to use, and help you deploy out to thousands of Remote Instances with the single click of a button.</p>
<h2 id="new-onboarding-tour" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#new-onboarding-tour"></a> New Onboarding Tour</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/free-tier-tour-HU-f_UwGIO-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/free-tier-tour-HU-f_UwGIO-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot from the new Free Tier onboarding tour" loading="lazy" decoding="async" src="https://flowfuse.com/img/free-tier-tour-HU-f_UwGIO-1920.jpeg" width="1920" height="1101" /></picture></p>
<p>We have a new onboarding tour that will be shown to those users signing up to our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/">new Free Tier on FlowFuse Cloud</a>.</p>
<p>This tour is purpose built to help you get up and running with your first Remote Instance in FlowFuse, setting up the Device Agent on your own hardware, and accessing your Remote Instance via FlowFuse Cloud.</p>
<h2 id="in-case-you-missed-it..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#in-case-you-missed-it..."></a> In Case You Missed It...</h2>
<p>Last month we <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/">released a new Free Tier on FlowFuse Cloud</a>, but announced it in the same article as our latest release and so a lot of the other new features we'd added to FlowFuse were lost in the weeds a little. We had a few self-hosted customers mention that they missed the updates for 2.12, so we also wanted to re-highlight some new features from that release too:</p>
<ul>
<li><strong>Pipelines</strong> - New view at the team-level to manage your DevOps Pipelines, making it easier to deploy between development, test and production environments. Pipelines can also be used to push updates out to thousands of Remote Instances at once using the new "Groups" feature.</li>
<li><strong>Bill of Materials</strong> - New view at the team-level to get a clear picture of all dependencies that your Instances are using. Makes it easier to manage out-of-date packages, and help with auditing and compliance.</li>
<li><strong>Device Agent Performance Improvements</strong> - For those running Node-RED 4.0.x and higher, the Device Agent has had some significant updates to performance, making the experience of remote editing much faster and smoother, especially when the device is on a slow network link.</li>
<li><strong>Dashboards in iFrames</strong> - FlowFuse-hosted Dashboards can now be configured to run inside iFrames. You can find this option under the "Settings" of the Instance in question.</li>
</ul>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#what-else-is-new%3F"></a> What Else Is New?</h2>
<p>For a full list of everything that went into our 2.13 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.13.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/flowfuse-release-2-13/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns-part-2/MQTT: The Frontrunner for Your UNS Broker - Part 2Why MQTT is the Best Choice for Your UNS Broker2025-01-13T00:00:00ZSumit Shinde<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/">Part 1</a>, we discussed the reasons behind MQTT's popularity as a choice for Unified Namespace (UNS) implementations; focusing on its lightweight design, low latency, and reliable message delivery. In this second part, we’ll explore additional factors that further establish MQTT as the leading protocol for UNS brokers, diving into its connectivity, scalability, structured topic management.</p>
<!--more-->
<h2 id="wide-connectivity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns-part-2/#wide-connectivity"></a> Wide Connectivity</h2>
<p>For your UNS to be truly effective, it must be able to connect seamlessly with every part of your IIoT environment—whether it’s a device or the cloud. MQTT is an excellent protocol for this because it’s widely adopted in both cloud-based systems and industrial environments.</p>
<p>In a typical industrial data architecture, you typically have a mix of physical devices (like sensors and machines) and cloud systems. To make real-time decisions, data needs to flow seamlessly from the shop floor to the cloud. MQTT excels at connecting modern devices with the cloud. However, one challenge is that it doesn’t natively support older systems that typically use protocols like OPC UA or MODBUS.</p>
<p>This isn’t a major issue, though. You can bridge the gap using tools like FlowFuse and Node-RED, which allow MQTT to communicate with older systems.</p>
<p>Overall, MQTT has better compatibility than many other protocols. Some other protocols may offer better compatibility, but they can hardly surpass MQTT in meeting the core requirements of a UNS.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-compatiblity-hLOCyrLkWM-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-compatiblity-hLOCyrLkWM-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img width="550px" data-zoomable="" alt="MQTT's Compatibility" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-compatiblity-hLOCyrLkWM-1920.jpeg" height="1078" /></picture>
<em>MQTT's Compatibility</em></p>
<h2 id="easily-scalable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns-part-2/#easily-scalable"></a> Easily Scalable</h2>
<p>A UNS broker needs to be able to grow as your factory or system expands. What starts with just a few sensors or devices might quickly scale up to hundreds or thousands over time. Can MQTT handle this growth? Absolutely.</p>
<p>MQTT's lightweight architecture, based on a simple publish-subscribe model, makes it easy to scale. Adding new devices doesn’t disrupt existing operations. The system can handle thousands of devices and connections without performance issues. As your network grows, new devices can easily connect to the broker and start exchanging data without major changes to the infrastructure.</p>
<p>MQTT brokers handle large volumes of data and can distribute messages to many clients at once. This ensures that as more devices are added, the system remains stable. The broker can also scale horizontally, meaning you can add more brokers or resources to handle the increased load, without affecting the performance or reliability of the system.</p>
<h2 id="semantic-hierarchy-with-topics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns-part-2/#semantic-hierarchy-with-topics"></a> Semantic Hierarchy with Topics</h2>
<p>When setting up a UNS, it’s not just about collecting all your data in one place—it’s about organizing it so that it’s easy to navigate. A semantic hierarchy helps do this by arranging data from broad categories down to more specific details. For example, in a factory, you might have levels like factory, area, line, and machine, with data points like temperature or humidity at the bottom. This is similar to how files are organized in folders, making it simple to find what you need.</p>
<p>MQTT works well for this kind of organization because its topics are already structured in a hierarchical way. Topics in MQTT are like paths that break down data into different levels, such as <code>factory/area-2/line-1/machine-5/temperature</code>. You can easily follow these paths to find the exact data you need. Furthermore, MQTT supports wildcard characters, which offer flexibility for subscriptions. The # wildcard matches all levels beneath a given level, so a subscription to <code>factory/#</code> will match any topic under the "factory" level, regardless of how deep the path goes. Similarly, the <code>+</code> wildcard matches only a single level, so <code>factory/+/line-1/machine-5/temperature</code> will match any topic that follows the same structure but with different areas.</p>
<p>While AMQP and Kafka also support topics and wildcards, they handle them differently. AMQP uses routing keys for message routing rather than a direct hierarchical topic structure like MQTT. Though AMQP has a topic exchange for routing based on patterns (similar to topics), it doesn’t provide the same natural hierarchical organization. Kafka, on the other hand, uses a flat topic model without a hierarchy, which can make it more difficult to maintain clarity and structure as the system grows in complexity. While both AMQP and Kafka can still be used in a UNS, their lack of a natural topic hierarchy makes them more challenging manage compared to MQTT, which provides a simpler, more intuitive way to organize and access data.</p>
<p>In conclusion, MQTT is an excellent choice for your UNS broker because it offers simplicity, scalability, and efficient data organization. Its lightweight design ensures smooth performance as your system grows, while the publish-subscribe model decouples producers and consumers, allowing them to operate independently. This reduces direct dependencies between devices and systems, making the overall architecture more flexible and easier to scale. MQTT’s hierarchical topic structure further simplifies data management and access. Compared to other options like AMQP and Kafka, MQTT provides a more intuitive, reliable, and scalable and most importantly simple solution for building a Unified Namespace that can adapt to future needs.</p>
<h2 id="build-your-uns-with-flowfuse-now" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns-part-2/#build-your-uns-with-flowfuse-now"></a> Build Your UNS with FlowFuse Now</h2>
<p>Now that you understand why MQTT is the best choice for your UNS, it’s time to build it. FlowFuse is the ideal platform for implementing your UNS with MQTT.</p>
<p>Please read our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">Building Your UNS with FlowFuse</a> where I show you how to build it quickly—just in 15 minutes!</p>
<p><strong>Why choose FlowFuse?</strong></p>
<p>FlowFuse is an industrial data platform that leverages the power of Node-RED, a popular open-source low-code platform for industrial automation. With over 5000 community-contributed nodes, it simplifies the process of collecting, transforming, and integrating data from a wide variety of industrial hardware and services, supporting nearly all industrial protocols. This means you have nearly everything you need to implement a UNS—all you need is the MQTT broker service.</p>
<p>Good news! Recently, FlowFuse has added a built-in MQTT broker service within the platform, making it even easier to manage your MQTT connections. This includes an interface for securely managing MQTT clients and, most importantly, a comprehensive topic hierarchy monitoring tool that’s ideal for managing your UNS.</p>
<p>FlowFuse not only allows you to build and manage your UNS, but also provides a collaborative environment where teams can work in real time. It offers scalability, security, and ease of use, making it simple to grow your system as your needs evolve.</p>
<p>With FlowFuse, you get everything you need to handle data pipelines, implement a UNS, and scale efficiently—all within a single platform.</p>
<div>
<p>Want to learn more about how FlowFuse can help?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=MQTT%3A%20The%20Frontrunner%20for%20Your%20UNS%20Broker%20-%20Part%202">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/MQTT: The Frontrunner for Your UNS Broker - Part 1Why MQTT is the Best Choice for Your UNS Broker2025-01-07T00:00:00ZSumit Shinde<p>Choosing the right broker for your UNS is crucial. It must support real-time data, scale easily, and integrate seamlessly with devices and services. MQTT often comes out as the best choice for these needs.</p>
<!--more-->
<p>In this first part of our series, we’ll explain why MQTT is the standout choice for UNS implementations. With its focus on lightweight, real-time messaging and robust reliability, MQTT delivers the performance IIoT environments demand, making it the perfect fit for a future-proof, scalable UNS.</p>
<p>The <a href="https://flowfuse.com/solutions/uns/">Unified Namespace (UNS)</a> is a data architecture (not just a tool or new technology) that centralizes and organizes data from various sources into a single, unified structure. It eliminates data silos by providing a standardized way to represent, access, and share information across different devices, systems, and services. For more information on what is UNS, read our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/">Introduction to the Unified Namespace</a>.</p>
<p>When choosing a broker for your UNS, it's crucial to consider how well the selected broker fits the specific requirements of your IIoT environment, including the types of devices and systems involved, as well as factors like scalability, reliability, and ease of integration. Several options are available alongside MQTT, including <a href="https://flowfuse.com/node-red/protocol/amqp/">AMQP</a>, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/">Kafka</a>, and cloud message brokers like AWS Kinesis and GCP Pub/Sub. While these alternatives offer unique features, MQTT stands out, and we’ll explain why later in this article. But If you’re interested in a brief overview of why these alternatives are not the best fit for UNS, check out our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/">Unified Namespace: What Broker to Use?</a></p>
<p>There is also an ongoing debate regarding the use of OPC-UA and other protocols for implementing UNS. While I won’t dive into this in detail here as i havent explored it much yet, I encourage you to start with the approach we call reverse engineering. First, understand why MQTT is the preferred choice for implementing UNS, and then explore how its features align with UNS needs. From there, you can evaluate whether other protocols offer similar capabilities. By following this process, you will find the right answer of your questions.</p>
<h2 id="background-of-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/#background-of-mqtt"></a> Background of MQTT</h2>
<p>Before diving into its specific advantages for UNS, let’s take a brief look at the history of <strong>MQTT</strong> and how it became the backbone of modern IoT communication.</p>
<p><a href="https://flowfuse.com/node-red/protocol/mqtt/">MQTT</a> was developed in the late 1990s by Andy Stanford-Clark at IBM and Arlen Nipper at Eurotech to address communication challenges in low-bandwidth, unreliable networks. This early focus on lightweight messaging paved the way for MQTT to become a pioneering solution for the rapidly expanding IoT space.</p>
<p>Since then, MQTT has evolved significantly. The protocol has gone through several iterations, from MQTT 3.1.1 to the more feature-rich MQTT 5.0, each version enhancing the protocol's capabilities to meet the demands of an increasingly connected world. Today, more than 25 years later, MQTT remains the <strong>de facto protocol</strong> for IoT applications, and its simplicity, scalability, and reliability continue to make it an ideal choice for industrial systems.</p>
<p>But, what exactly makes MQTT the frontrunner for UNS implementations? Let's take a deeper look at some of its key features?</p>
<h2 id="publish-subscribe-model-and-event-driven-architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/#publish-subscribe-model-and-event-driven-architecture"></a> Publish-Subscribe Model and Event-Driven Architecture</h2>
<p>One of the standout features of MQTT is its publish-subscribe (Pub/Sub) model, which is the primary need of an UNS. In this model, data producers (such as sensors or devices) don’t need to know who is receiving the data or how many consumers are out there. Instead, they publish their data to a central broker, and any consumer (like a monitoring system, data warehouse, or analytics engine) that is interested simply subscribes to the relevant data stream.</p>
<p>This approach decouples producers and consumers, removing the need for direct, point-to-point connections between them. In traditional systems, every device would need to know about every other device it communicates with, leading to a messy, tightly coupled network. As your IIoT ecosystem grows, managing these connections becomes increasingly difficult and prone to error. But with MQTT’s Pub/Sub model, adding new devices or services is seamless and doesn’t disrupt existing data flows.</p>
<p>Beyond this, MQTT’s event-driven architecture takes the system to a whole new level of efficiency and responsiveness. Imagine a scenario where a machine detects an issue—rather than waiting for a periodic check-in, the event is immediately sent to the right consumer, triggering an alert in real time. This push mechanism is far more efficient than traditional polling, where systems continuously ask, “Is there new data yet?” and waste precious resources in the process.</p>
<p>With MQTT, data is pushed as soon as it’s available, enabling faster decision-making and real-time responses. This means events like machine faults or environmental changes are addressed immediately, making the Our UNS more agile, responsive, and capable of scaling as needed which is one of priamary need of IIoT environemnt.</p>
<p>If you’d like to understand the importance of Publish-Subscribe (Pub/Sub) architecture in detail for UNS, I highly recommend reading our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/">Why UNS Needs Pub/Sub</a>.</p>
<h3 id="low-latency-and-lightweight-messaging" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/#low-latency-and-lightweight-messaging"></a> Low Latency and Lightweight Messaging</h3>
<p>Downtime in industrial operations can be very costly—ranging from $15,000 to $20,000 per minute or more. For engineers and operators watching over machines, waiting for data can mean the difference between smooth operations and expensive disruptions, If you're interested in learning more about downtime, read this research by Siemens from 2022: <a href="https://assets.new.siemens.com/siemens/assets/api/uuid:3d606495-dbe0-43e4-80b1-d04e27ada920/dics-b10153-00-7600truecostofdowntime2022-144.pdf">True Cost of Downtime 2022</a>. Low-latency messaging is key in these situations, which is why data architectures like UNS are being explored to make sure systems and devices in your IIoT setup communicate without delays. MQTT is the protocol that makes this possible.</p>
<p>As we explored, MQTT uses a publish-subscribe model, which is built for real-time communication with minimal delay. Unlike traditional request-response systems that can cause delays due to constant querying, MQTT keeps a persistent connection open. Once a device connects, it can immediately send data or receive updates on important topics, cutting out the need for repeated requests and making sure data flows instantly. This helps engineers make decisions and take action faster.</p>
<p>In addition to being fast, MQTT is very efficient. Its messages are small and use little bandwidth—important when working with low-bandwidth networks or many connected devices. Even with limited resources, MQTT allows devices to send data without overwhelming the system. The result? As soon as a sensor detects a change—like a temperature spike or a production issue—it can send the information right away to the right system, triggering immediate actions to avoid costly downtime.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-650.avif 650w, https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-650.webp 650w, https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-650.jpeg 650w, https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="MQTT Packet Structer" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-packate-size-MLMhOMLOjV-650.jpeg" width="1300" height="469" /></picture> <em>MQTT Packet Structer</em></p>
<h3 id="reliability" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2025/01/mqtt-frontrunner-for-uns/#reliability"></a> Reliability</h3>
<p>When it comes to building a UNS, reliability is absolutely crucial. Missing or duplicate data can lead to poor decision-making, system malfunctions, or even costly downtime—things no one wants in their IIoT environment.</p>
<p>This is where MQTT truly shines. It’s built with a Quality of Service (QoS) mechanism that allows you to control how reliably your messages are delivered. Depending on the level you choose, you can ensure that data is delivered exactly as you need it, without compromising on system performance.</p>
<p>MQTT offers three levels of QoS to suit different use cases:</p>
<ul>
<li>QoS 0 - "At most once": The message is delivered once and isn’t acknowledged. This is fine for non-critical data where the occasional loss of a message is acceptable.</li>
<li>QoS 1 - "At least once": The message is delivered at least once, with an acknowledgment to ensure it was received. This is ideal for most IIoT applications, where you need reliable delivery, but duplicate messages are not a major concern.</li>
<li>QoS 2 - "Exactly once": This guarantees the message is delivered exactly once—no duplicates, no omissions. It's the best choice for mission-critical applications where data integrity is paramount.</li>
</ul>
<p>Now, some other protocols like AMQP or Kafka also provide reliability guarantees, but they tend to be more complex and come with heavier infrastructure requirements. MQTT, on the other hand, offers a simple and lightweight design while still giving you just the right level of reliability for most IIoT scenarios. You can scale your network with ease, all while maintaining a high standard of reliability in your data flows.</p>
<p>MQTT is the ideal broker for a UNS in IIoT environments, offering real-time, low-latency communication through its Publish-Subscribe model. With Quality of Service (QoS) options for reliable data delivery, it balances performance, scalability, and simplicity. MQTT’s lightweight design makes it perfect for handling large-scale, mission-critical data flows without the complexity of heavier protocols.</p>
<p>In Part 2, we will explore MQTT's scalability, topic organization, and wide connectivity providing even more compelling reasons why it is the ultimate choice for UNS brokers.</p>
<p>If you're looking to build your own UNS with MQTT, check out our step-by-step <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">Article on Building UNS with FlowFuse</a>. Plus, we've made it even easier by offering a built-in MQTT broker service within the FlowFuse Platform, allowing you to manage all your MQTT clients, devices, services, and data from a single, centralized interface. Check it out <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20mqtt%20announcement&utm_term=high_intent&utm_content=MQTT%3A%20The%20Frontrunner%20for%20Your%20UNS%20Broker%20-%20Part%201/">here</a>!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/FlowFuse Cloud now available for free!With our new FlowFuse release, comes a new team tier, available on FlowFuse Cloud, to provide you an easy way to manage your many Node-RED instances.2024-12-19T00:00:00ZGreg Stoutenburg<p>The new Free plan on FlowFuse Cloud will allow you to manage two remote instances using FlowFuse Device Agent, completely free of charge, forever! The new plan also provides Device Auto Snapshots, so any changes to your Node-RED flows running on your devices are backed up automatically.</p>
<!--more-->
<p>FlowFuse is an industrial data platform that enables engineers to build, manage, scale, and secure their Node-RED solutions for digitalizing processes and operations. More fundamentally though, it's a great platform to manage multiple instances of Node-RED.</p>
<h2 id="what's-included%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#what's-included%3F"></a> What's Included?</h2>
<p>Whether you're running multiple Node-RED instances at home for Home Automation, or running thousands of Node-RED instances in your factory, FlowFuse provides an easy-to-use, centralised view of your Node-RED instances, making it easy to manage and monitor them in one place.</p>
<p>In our new free tier on FlowFuse Cloud you get:</p>
<ul>
<li>2 x Remote Node-RED Instances, managed through the FlowFuse <a href="https://flowfuse.com/docs/device-agent/quickstart/">Device Agent</a></li>
<li>Version control for your Node-RED flows with FlowFuse's <a href="https://flowfuse.com/docs/user/snapshots/#introduction">Snapshots</a></li>
<li>Remote access to your Node-RED instances through FlowFuse Cloud, utilizing the Device Agent's <a href="https://flowfuse.com/docs/device-agent/quickstart/#developer-mode">Developer Mode</a></li>
</ul>
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#getting-started"></a> Getting Started</h2>
<h3 id="create-your-free-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#create-your-free-team"></a> Create Your Free Team</h3>
<p>To create a free team, simply <a href="https://app.flowfuse.com/create">sign up to FlowFuse Cloud</a>.</p>
<p>Once you've filled in your details, you'll be presented with the option to choose your team type. Select the "Free" option, and you're good to go.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/onboarding-team-type-d4TME7KHM6-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/onboarding-team-type-d4TME7KHM6-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/onboarding-team-type-d4TME7KHM6-1920.jpeg" width="1920" height="1102" /></picture>
<em>Screenshot showing the UI for selecting your Team's type, when onboarding through on FlowFuse Cloud.</em></p>
<!-- <img width="438" alt="image" src="https://github.com/user-attachments/assets/da6fde55-27bc-42d7-afcc-19235661b558" /> -->
<h3 id="remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#remote-instances"></a> Remote Instances</h3>
<p>When you create a Free team, an Application is created for you automatically. Applications in FlowFuse help you organise and group your resources, from Instances to DevOps Pipelines and Snapshots for Version Control. Within your new application, you can "register" your first Remote Instance.</p>
<p>A "Remote Instance" in FlowFuse is a just a term for an instance Node-RED, managed by FlowFuse, but running somewhere different from FlowFuse, e.g. on Edge hardware in a factory or your home. FlowFuse connects to these Remote Instances using the FlowFuse Device Agent, which is easy to setup and get running.</p>
<p>To get started with your Remote Instance, you need to complete two steps:</p>
<ol>
<li><strong>Install Device Agent</strong>: The FlowFuse Device Agent is installed onto the hardware where you want your Node-RED Instance to run.</li>
<li><strong>Add Your Remote Instance</strong>: In the FlowFuse UI, add a new "Remote Instance", and connect to your hardware.</li>
</ol>
<h3 id="install-device-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#install-device-agent"></a> Install Device Agent</h3>
<p>Firstly, wherever you want your Node-RED to run, e.g. on a Raspberry Pi or your own Laptop, install the <code>flowfuse-device-agent</code> package:</p>
<h4 id="linux%2Fmacos" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#linux%2Fmacos"></a> Linux/MacOS</h4>
<div style="position: relative" id="code-container-82">
<pre class="language-bash"><code id="code-82" class="language-bash"><span class="token function">sudo</span> <span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> @flowfuse/device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-82" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="windows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#windows"></a> Windows</h4>
<p>Issue the below command in an elevated command prompt:</p>
<div style="position: relative" id="code-container-89">
<pre class="language-bash"><code id="code-89" class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> @flowfuse/device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-89" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>For alternative installation options and more details, please refer to our <a href="https://flowfuse.com/docs/device-agent/">documentation</a>.</p>
<h3 id="connect-your-hardware-to-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#connect-your-hardware-to-flowfuse"></a> Connect Your Hardware to FlowFuse</h3>
<p>To connect your Device Agent, in the FlowFuse Platform, click on the "Remote Instances" option in the left-hand menu, and then click the "Add Remote Instance" button.</p>
<p>Fill out the name, device type and select the application you've just created, and you'll be presented with the following:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/onboarding-device-registration-j2rfvM77QH-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/onboarding-device-registration-j2rfvM77QH-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the dialog with the one-time-code to connect your remote Node_RED instance to FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/onboarding-device-registration-j2rfvM77QH-1920.jpeg" width="1920" height="1102" /></picture>
<em>Screenshot showing the dialog with the one-time-code to connect your remote Node_RED instance to FlowFuse</em></p>
<p>The command presented is used to connect your device to FlowFuse. Run this where you just installed the <code>@flowfuse/device-agent</code> package. This will connect your remote Node-RED instance to the FlowFuse platform.</p>
<p>The final step to take is to then start running the device agent, which you can do by simply calling:</p>
<div style="position: relative" id="code-container-111">
<pre class="language-bash"><code id="code-111" class="language-bash">flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-111" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="developing-on-your-remote-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#developing-on-your-remote-instance"></a> Developing on your Remote Instance</h3>
<p>FlowFuse offers remote development capabilities, allowing you to edit your Node-RED flows directly, and securely, from the FlowFuse Cloud platform. This is done through "Developer Mode".</p>
<p>To get started with developing flows with your Remote Instance, simply select your newly created Instance in the FlowFuse UI, toggle on 'Developer Mode", and click "Open Editor"!</p>
<h3 id="version-control-with-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#version-control-with-snapshots"></a> Version Control with Snapshots</h3>
<p>FlowFuse makes version control easy with <a href="https://flowfuse.com/docs/user/snapshots/#introduction">Snapshots</a>. The new Free plan includes Device Auto Snapshots, which will automatically create a snapshot any time a flow is deployed to your device.</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#what-else-is-new%3F"></a> What Else is New?</h2>
<p>In addition to the Free plan, we've also added two new views to the FlowFuse platform:</p>
<ul>
<li><strong>Pipelines</strong>: This view provides a way to manage all of your DevOps Pipelines, making it even easier to manage your development, testing, staging and production environments.</li>
<li><strong>Bill of Materials</strong>: This view provides a way to manage all of your Node-RED dependencies, making it easy to see what versions of nodes are being used across all of your Node-RED instances.</li>
</ul>
<p>For a full list of everything that went into our 2.12 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.12.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-release-2-12/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/Data Modeling: The Key to a Successful Unified NamespaceWhy data modeling is key to making your Unified Namespace work effectively.2024-12-16T00:00:00ZSumit Shinde<p>In manufacturing, data flows from various sources—machines, sensors, enterprise systems, and more. A <a href="https://flowfuse.com/solutions/uns/">Unified Namespace (UNS)</a> brings all this data into a central hub. However, centralizing data isn't enough to truly call it a UNS. It's not just about aggregating information. A true UNS goes beyond being a data repository; it organizes, structures, and contextualizes that data, transforming it into something valuable and actionable for your business.</p>
<!--more-->
<p>By applying a solid data model, you can turn scattered, unstructured data into a cohesive, meaningful system that supports better decision-making and drives operational improvements. Let's explore what data modeling is, why it is crucial for making your UNS function effectively, and how it turns scattered data into valuable insights.</p>
<p><em>At its core, <strong>data modeling</strong> is designing how data will be structured, organized, and stored within the UNS. It’s about creating a blueprint or framework that defines the relationships between different data points, ensuring they’re logically structured, easily accessible, and aligned with the business's needs.</em></p>
<p>A solid data model forms the foundation for understanding and using data effectively. It addresses essential questions like:</p>
<ul>
<li><strong>How should data be represented?</strong> – What formats, units, or categories should be used to express the data?</li>
<li><strong>When is it captured, and what does it represent?</strong> – Is it sensor data, performance metrics, or supply chain information? When was it recorded, and what is its context?</li>
</ul>
<p>The data model can vary significantly from business to business, depending on the use case. For example, in manufacturing, if a device is sending sensor metrics to a UNS, the data could look like this:</p>
<div style="position: relative" id="code-container-28">
<pre class="language-json"><code id="code-28" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token string">"2024-09-19T12:33:46.6035772+02:00"</span><span class="token punctuation">,</span><br /> <span class="token property">"machineId"</span><span class="token operator">:</span> <span class="token string">"press_001"</span><span class="token punctuation">,</span><br /> <span class="token property">"manufacturer"</span><span class="token operator">:</span> <span class="token string">"XYZ Corp"</span><span class="token punctuation">,</span><br /> <span class="token property">"model"</span><span class="token operator">:</span> <span class="token string">"MPX-5000"</span><span class="token punctuation">,</span><br /> <span class="token property">"sensors"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"vibration"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">1.5</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mm/s"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"temperature"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">72.4</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"Celsius"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"pressure"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">3.8</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"Bar"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-28" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Alternatively, a simpler model might look like this:</p>
<div style="position: relative" id="code-container-32">
<pre class="language-json"><code id="code-32" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token string">"2024-09-19T12:33:46+02:00"</span><span class="token punctuation">,</span><br /> <span class="token property">"machine"</span><span class="token operator">:</span> <span class="token string">"press_001"</span><span class="token punctuation">,</span><br /> <span class="token property">"vibration"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">1.5</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"mm/s"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"temperature"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">72.4</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"Celsius"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"pressure"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">3.8</span><span class="token punctuation">,</span> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"Bar"</span> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-32" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>In both examples, the data is structured with critical elements like timestamps, machine identifiers, sensor readings, and units of measurement. The crucial difference between the two models lies in the level of detail, but both demonstrate how a well-defined data modeling adds immediate value. Organizing data clearly and consistently ensures its accuracy and enables it to be easily analyzed, integrated, and acted upon.</p>
<p>Now that you have a basic understanding of what data modeling is Let’s take a deep dive into why data modeling is essential to unlocking the full potential of your UNS:</p>
<h3 id="1.-ensures-data-consistency-and-standardization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#1.-ensures-data-consistency-and-standardization"></a> 1. Ensures Data Consistency and Standardization</h3>
<p>As mentioned earlier, data is generated from various sources in manufacturing, including machines, sensors, ERP systems, and inventory management tools. Each source may provide data in different formats, units of measurement, or naming conventions.</p>
<p>For example, one machine might report temperature in Celsius, while another uses Fahrenheit. Some systems might track production rates in pieces per hour, while others use units per minute. These inconsistencies can create confusion and lead to errors in a computerized structure.</p>
<p>Data modeling addresses this issue by establishing clear standards for how data should be structured and labeled. It ensures uniform temperature, unit, and measurement formats across all systems. For example, a data model might require recording all temperature readings in Celsius and production rates in pieces per hour. This consistency simplifies data analysis from diverse sources and ensures that the system functions reliably and accurately.</p>
<h3 id="2.-gives-data-meaning" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#2.-gives-data-meaning"></a> 2. Gives Data Meaning</h3>
<p>Raw data on its own is just numbers—isolated and incomplete. For example, a temperature reading of 72.4°C or a vibration level of 1.5 mm/s doesn’t tell much without the proper context. This is where data modeling truly adds value. A data model turns simple measurements into meaningful insights by organizing data that includes critical details like timestamps, data sources, and relationships between data points.</p>
<p>Contextualizing data means understanding when it was captured, where it came from, and what it’s related to. A timestamp helps track trends over time, such as detecting a gradual temperature rise that could signal an issue before it becomes critical. Knowing the data source—whether it’s a specific machine or sensor—enables targeted troubleshooting and ensures that the right teams are working with the right data. Data modeling also links data points, like correlating vibration and temperature readings, to identify potential equipment failures. This structure makes data far more accessible and actionable, allowing teams to make informed, real-time decisions, prevent unplanned downtime, and drive process improvements.</p>
<p>In summary, data modeling transforms raw data into actionable insights, helping you understand what is happening in your operation, when it started, where it is occurring, and how to address it effectively.</p>
<h3 id="3.-facilitates-data-interoperability-and-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#3.-facilitates-data-interoperability-and-integration"></a> 3. Facilitates Data Interoperability and Integration</h3>
<p>In manufacturing, various devices and systems generate data in different formats and use distinct communication protocols, making integrating and utilizing this data effectively challenging.</p>
<p>For example, PLCs may use one protocol, while SCADA systems or MES may rely on entirely different ones. This lack of consistency complicates consolidating data and extracting meaningful insights. Integrating other systems often requires addressing discrepancies in how data is structured and labeled. For instance, multiple sensors on a production line might send data in various formats or label the same metric differently. One sensor might label temperature as "temp," another as "temperature," and yet another as "T1." These inconsistencies can lead to errors or failures in integrated systems, such as monitoring tools that depend on consistent data labeling to function correctly.</p>
<p>This is where data modeling becomes essential. It creates a standard structure for organizing and labeling data, ensuring that different systems can "speak the same language" and integrate seamlessly.</p>
<p>For example, in a predictive maintenance system, sensor data such as temperature and vibration can be used to predict potential machine failures. With the right data model, this sensor data can be directly linked to your CMMS (Computerized Maintenance Management System), regardless of how many sensors are involved or added over time. Since engineers understand the standardized data structure, they can easily integrate the CMMS with minimal effort. This integration automatically triggers maintenance alerts and work orders, helping to prevent downtime without requiring manual intervention.</p>
<h3 id="4.-enables-easy-access-and-time-savings" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#4.-enables-easy-access-and-time-savings"></a> 4. Enables Easy Access and Time Savings</h3>
<p>Disorganized data wastes time and resources in manufacturing. A transparent data model makes essential information easy for troubleshooting, performance checks, and decision-making tasks.</p>
<p>With a standardized system, figuring out confusing labels or complex data is unnecessary. A consistent data model organizes information, helping operators respond faster, reduce downtime, and minimize errors.</p>
<p>Quick access to accurate data speeds up decision-making, cuts downtime, and improves efficiency, leading to cost savings.</p>
<h3 id="5.-scalability-and-continuous-improvement" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#5.-scalability-and-continuous-improvement"></a> 5. Scalability and Continuous Improvement</h3>
<p>A well-structured data model serves the needs of your current manufacturing operations and lays the groundwork for future growth and continuous improvement. As your production processes evolve or new technologies and data sources come online, a good data model allows your UNS to adapt without disrupting existing systems.</p>
<p>For example, as you add new machines, automated production lines, or advanced sensors to your operations, the data model ensures that these new elements integrate smoothly into the existing framework. It maintains consistency, enabling new data points to be mapped easily while keeping the system organized and efficient.</p>
<hr style="border: none; border-top: 3px solid rgba(173, 192, 252, 0.55); opacity: 0.3; margin-bottom: 20px;" />
<p>With all these benefits in mind, the next step is to consider how to implement data modeling effectively. Data modeling is a much deeper, more dynamic process that can reshape how you use data across your entire business. It's not only about structuring and integrating data but also about transforming your operations, unlocking new efficiencies, and driving growth. The right platform can help you go beyond essential organization and truly harness the power of your data.</p>
<h2 id="leverage-flowfuse-for-effective-data-modeling-in-your-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/why-uns-need-data-modeling/#leverage-flowfuse-for-effective-data-modeling-in-your-uns"></a> Leverage FlowFuse for Effective Data Modeling in Your UNS</h2>
<p><a href="https://flowfuse.com/">FlowFuse</a> makes building and managing a Unified Namespace (UNS) simple and efficient. It connects IT and OT systems, streamlines workflows, and transforms raw data into meaningful insights. With FlowFuse, you can:</p>
<ul>
<li><strong>Connect</strong>: Integrate various services, hardware, and APIs effortlessly.</li>
<li><strong>Collect</strong>: Aggregate data from machines, sensors, and other sources.</li>
<li><strong>Transform</strong>: Easily standardize and add context to raw data, making it more meaningful and usable across your systems.</li>
<li><strong>Visualize</strong>: Create dashboards with a low-code approach to monitor and analyze your operations.</li>
</ul>
<p>Learn how to build your UNS in just 15 minutes with <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">this article</a>.</p>
<p>FlowFuse uses Node-RED, an open-source low-code platform, to turn unstructured data into organized, actionable models. Its ability to transform and contextualize data ensures consistency and clarity, helping you get the most value from your data.</p>
<p>With support for over 5,000 community nodes and protocols like MQTT, OPC-UA, and Modbus, FlowFuse simplifies connecting systems and unifying data.</p>
<p>Check out <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/">this article</a> to see how FlowFuse makes data modeling easier.</p>
<p>FlowFuse also offers enterprise-grade features to manage edge devices and Node-RED instances, helping you scale, collaborate, and stay compliant.</p>
<p><strong>Take Control of Your Data with FlowFuse</strong></p>
<div>
<p>Want to make the most of your data? Our team is ready to help you set up a Unified Namespace (UNS) that fits your needs.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=Data%20Modeling%3A%20The%20Key%20to%20a%20Successful%20Unified%20Namespace">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/Streamlining Node-RED Collaboration with FlowFuseEnabling Real-Time Collaboration in Industrial Environments with FlowFuse2024-12-09T00:00:00ZSumit Shinde<p>A few weeks ago, we discussed how <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/">FlowFuse centralizes edge device and Node-RED management</a>, as well as its <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/">security features</a>. Now, we're focusing on another key benefit of FlowFuse—making collaboration easier for teams in industrial environments.</p>
<!--more-->
<p>As more manufacturing companies use FlowFuse for edge device management, data pipelines, and bridging IT-OT systems, the platform is becoming a key tool for handling complex data operations. Collaboration is critical in manufacturing environments with thousands of devices and data flows. FlowFuse accelerates industrial data operations by simplifying real-time collaboration while ensuring security and scalability.</p>
<p>Now, let’s look at how FlowFuse makes collaboration effortless. With features like centralized management, real-time updates, and easy sharing, FlowFuse helps teams stay on the same page—whether working on the same flow or across multiple projects. The platform eliminates the usual pain points of collaboration, allowing teams to quickly deploy, modify, and scale Node-RED solutions confidently while maintaining control and security.</p>
<h2 id="laying-the-foundation-for-better-collaboration-with-centralization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#laying-the-foundation-for-better-collaboration-with-centralization"></a> Laying the Foundation for Better Collaboration with Centralization</h2>
<p>Effective collaboration starts with everyone having access to the same interface and information. When team members work with different versions of flows or data, things can easily get out of sync, making collaboration difficult.</p>
<p>This is exactly why centralized management is so crucial. With FlowFuse, everything—your Node-RED Instances, edge devices, settings, and data—is in one place. Everyone on your team sees the same interface, accesses the same up-to-date information, and works from the same set of resources. There’s no more hunting for the correct version or trying to sync up tools.</p>
<p>Everything in one place means fewer mistakes, fewer mix-ups, and much easier collaboration. Whether you're working in a single instance or managing a whole network of edge devices, FlowFuse makes it easy to stay aligned. You can move quickly and confidently without worrying about missing something important.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/application-wise-instance-organization-JzaxNzCQFL-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/application-wise-instance-organization-JzaxNzCQFL-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="List of all Node-RED instances organized centrally within your team." loading="lazy" decoding="async" src="https://flowfuse.com/img/application-wise-instance-organization-JzaxNzCQFL-1600.jpeg" width="1600" height="943" /></picture>
<em>A centralized view of applications with Node-RED instances organized under each application for easier management.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/list-of-instances-lYhW2P1WSZ-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/list-of-instances-lYhW2P1WSZ-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="List of all Node-RED instances organized centrally within your team." loading="lazy" decoding="async" src="https://flowfuse.com/img/list-of-instances-lYhW2P1WSZ-1600.jpeg" width="1600" height="938" /></picture>
<em>A list of all Node-RED instances is displayed centrally, allowing team members to manage and access them easily.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edge-devices-rKTweG-7Sz-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edge-devices-rKTweG-7Sz-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="All edge devices centralized for easy management." loading="lazy" decoding="async" src="https://flowfuse.com/img/edge-devices-rKTweG-7Sz-1600.jpeg" width="1600" height="939" /></picture>
<em>A centralized view of edge devices, providing streamlined management for all devices within the platform.</em></p>
<p>In short, centralization makes everything simpler. It’s the backbone of effective teamwork, ensuring everyone works from the same starting point. With FlowFuse, you don't have to worry about syncing — everything's already in sync for you.</p>
<p>If you want to dive deeper into how FlowFuse centralizes things, check out this article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/">Managing Node-RED Instances in a Centralized Platform</a>.</p>
<h2 id="creating-and-managing-teams" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#creating-and-managing-teams"></a> Creating and Managing Teams</h2>
<p>Collaboration begins with the right team setup. In FlowFuse, you can quickly <a href="https://flowfuse.com/docs/user/team/#creating-a-new-team">create a team</a> and assign it a name. Once your team is set up, you can easily <a href="https://flowfuse.com/docs/user/team/#adding-team-members">invite members to join</a>. As mentioned earlier, all your Node-RED instances, edge devices, configurations, settings, and data are organized and centralized within that team.</p>
<p>However, simply creating a team and inviting members isn’t enough. Not all team members need access to all information, features, or controls. It’s essential to ensure that the right people have the right level of access to do their work effectively. Providing excessive access to members who don’t require it or who might lack the necessary expertise can lead to accidental changes or mistakes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/inviting-member-to-team-BAs-n04bDt-585.avif 585w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/inviting-member-to-team-BAs-n04bDt-585.webp 585w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Invite new members to your FlowFuse team by assigning roles and permissions." loading="lazy" decoding="async" src="https://flowfuse.com/img/inviting-member-to-team-BAs-n04bDt-585.jpeg" width="585" height="438" /></picture>
<em>A centralized view of edge devices, providing streamlined management for all devices within the platform.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/team-members-list-mt80xro3Wd-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/team-members-list-mt80xro3Wd-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse team management interface displaying team members and their roles for streamlined collaboration control." loading="lazy" decoding="async" src="https://flowfuse.com/img/team-members-list-mt80xro3Wd-1600.jpeg" width="1600" height="938" /></picture>
<em>A comprehensive list of team members with assigned roles and permissions</em></p>
<p>This is where <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">FlowFuse’s Role-Based Access Control (RBAC)</a> comes in. When inviting new team members, you can assign them specific roles, each with different permissions and access scopes. This ensures that each team member has the appropriate level of access based on their role, reducing the risk of unintended changes or disruptions.</p>
<p>You can later update roles to adjust permissions as your team, and projects evolve if needed. FlowFuse’s RBAC helps maintain control and security while empowering each team member to contribute effectively to the project.</p>
<h2 id="real-time-collaboration-and-monitoring" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#real-time-collaboration-and-monitoring"></a> Real-Time Collaboration and Monitoring</h2>
<p>Once team members have been invited, Node-RED instances have been created, and edge devices have been added by the team owner, FlowFuse's real-time collaboration capabilities come into play. Team members, whether they have owner or member roles, can work on the same project simultaneously. They can update flows, deploy them, and monitor real-time progress.</p>
<p>With FlowFuse, tracking who is working on which project and at what stage is easy. The platform lets you see who has opened the Node-RED editor you are working on, which flow they are working on, and even which specific node they are interacting with. This visibility ensures that team members are always aligned and that collaboration happens seamlessly without stepping on each other’s toes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multiplayer-flowfuse-eVJR5jPYQd-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multiplayer-flowfuse-eVJR5jPYQd-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot showing multiple users working on the same Node-RED flow in real time within FlowFuse, with live updates and visibility of each user's actions." loading="lazy" decoding="async" src="https://flowfuse.com/img/multiplayer-flowfuse-eVJR5jPYQd-1600.jpeg" width="1600" height="771" /></picture>
<em>Multiple users collaborate on the same Node-RED flow in real-time within FlowFuse.</em></p>
<p>For instance, if two members modify different parts of the same flow, they can do so without interrupting each other’s work. They’ll both be able to see each other's updates in real-time, ensuring the project moves forward smoothly. This collaborative approach enhances teamwork and helps avoid mistakes, as everyone has the information they need when needed.</p>
<p>In addition, FlowFuse’s real-time monitoring features allow you to track the status of edge devices, Node-RED instances, and overall system health. You can also monitor Node-RED logs for troubleshooting, ensuring no issue slips through the cracks as your team works together efficiently.</p>
<h2 id="version-controlling" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#version-controlling"></a> Version Controlling</h2>
<p>In any collaborative environment, accidental changes or conflicts are inevitable, especially when multiple team members are working on the same project. While collaboration is essential for progress, the ability to track changes, roll back versions, and understand who made what updates is critical for maintaining control and continuity.</p>
<p>FlowFuse provides robust version control features that make it easier to manage changes across your Node-RED flows. Whether you revert to a previous version of a flow, roll back accidental changes, or track updates over time, FlowFuse ensures that you never lose critical work.</p>
<p>To view the version history, navigate to your Node-RED Instance view and switch to the 'Version History' tab. Here, you’ll find two key sections:</p>
<ul>
<li>Timeline: This section displays a timeline of who deployed, what is updated, and for which flow and when. Each deployment automatically creates a snapshot of your Node-RED instance. You can easily roll back to any previous version by clicking the three-dot icon on the right and selecting Restore Snapshot. You also have the option to compare the current version of your Node-RED instance with previous snapshots, download it, and more.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/version-history-timeline-dIwI8i20RK-1436.avif 1436w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/version-history-timeline-dIwI8i20RK-1436.webp 1436w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot displaying the version history timeline in FlowFuse, showing deployment snapshots and the ability to track and roll back changes in Node-RED instances." loading="lazy" decoding="async" src="https://flowfuse.com/img/version-history-timeline-dIwI8i20RK-1436.jpeg" width="1436" height="540" /></picture>
<em>A version history timeline shows deployment and changes made in flows, making it easy to track updates and revert to previous versions.</em></p>
<p>Snapshots: The second tab, Snapshots, provides a clean, list-style interface that shows all available snapshots. Unlike the timeline view, this section focuses solely on snapshots without the detailed deployment history. You can upload or download snapshots and even create a snapshot of your instance at any time by clicking the Create Snapshot button.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/version-history-snapshots-zS8DKbN3Au-1435.avif 1435w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/version-history-snapshots-zS8DKbN3Au-1435.webp 1435w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot of the snapshots interface in FlowFuse, showing a list of available snapshots that can be restored or compared." loading="lazy" decoding="async" src="https://flowfuse.com/img/version-history-snapshots-zS8DKbN3Au-1435.jpeg" width="1435" height="519" /></picture>
<em>The snapshots list interface in FlowFuse provides an organized view of all available snapshots for easy restoration or comparison.</em></p>
<p>This version control functionality allows you to manage and recover Node-RED instances seamlessly, ensuring that your team can collaborate effectively without the risk of losing or overwriting important work.</p>
<h2 id="shared-flow-library" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#shared-flow-library"></a> Shared Flow Library</h2>
<p>When working on projects, it's common to develop reusable flows that can save time and effort for the entire team. For example, you might create a flow to calculate Overall Equipment Efficiency (OEE) or other valuable flows that can be reused across multiple projects.</p>
<p>FlowFuse makes this process easy with the <a href="https://flowfuse.com/docs/user/shared-library/">Shared Flow Library</a>. This feature allows owners and members to export significant flows and store them in a shared library, making them available to all team members. Once a flow is added to the library, anyone within the same team can import and use it in any Node-RED instance whenever needed.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/shared-lib-import-W_yond_VZL-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/shared-lib-import-W_yond_VZL-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot of the Shared Flow Library in FlowFuse, showing a list of flows available for import and reuse by team members in any Node-RED instance." loading="lazy" decoding="async" src="https://flowfuse.com/img/shared-lib-import-W_yond_VZL-1300.jpeg" width="1300" height="1087" /></picture>
<em>Shared Flow Library in FlowFuse displays a list of reusable flows that team members can import and use across multiple Node-RED instances.</em></p>
<p>This shared approach streamlines development, eliminates redundant work, and ensures project consistency, ultimately saving time. Whether it’s a reusable function, a typical configuration, or a flow for recurring tasks, FlowFuse’s Shared Flow Library enables your team to easily access and integrate these resources, boosting productivity and fostering better collaboration.</p>
<h2 id="audit-logs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#audit-logs"></a> Audit Logs</h2>
<p>Accountability is essential when multiple team members collaborate on complex industrial projects. FlowFuse offers comprehensive Audit Logs to track every action taken within the platform. This feature provides a detailed record of who made which changes, when those changes were made, and the specifics of each action.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/application-audit-logs-mg1IO2oUnn-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/application-audit-logs-mg1IO2oUnn-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A screenshot of the audit logs in FlowFuse, displaying detailed records of changes made by users, ensuring accountability and security within the platform." loading="lazy" decoding="async" src="https://flowfuse.com/img/application-audit-logs-mg1IO2oUnn-1600.jpeg" width="1600" height="589" /></picture>
<em>Audit logs in FlowFuse, tracking every action taken within the platform to ensure accountability and transparency among team members.</em></p>
<p>With Audit Logs, you can easily trace who deployed a flow, modified settings, or interacted with assets. This transparency ensures that all team members are accountable for their actions, enhancing security and reducing the risk of errors or unauthorized changes.</p>
<p>Audit Logs are especially valuable for troubleshooting. They allow you to pinpoint when issues arise and identify who made changes that could have contributed to the problem, providing crucial context for resolution.</p>
<p>For more information on using Audit Logs effectively, refer to our documentation on <a href="https://flowfuse.com/docs/user/logs/#audit-log">Audit Logs in FlowFuse</a>.</p>
<p>These are just a few of the features we’ve covered. FlowFuse has many more tools and capabilities that can further enhance collaboration, streamline workflows, and optimize your industrial operations.</p>
<p>In summary, FlowFuse is a platform that streamlines collaboration on Node-RED projects. Centralizing your Node-RED instances, devices, and data ensures that everyone on your team is aligned and has access to the same resources. Features like real-time updates, role-based access, version control, and audit logs make collaborating easier, staying secure, and avoiding errors easier. Whether you're managing small flows or large-scale industrial systems, FlowFuse helps your team work together more efficiently and effectively.</p>
<p>With FlowFuse, factories can achieve faster, safer, and more scalable collaboration on data pipelines, edge device management, and IT-OT integration.</p>
<p><em>Ready to Improve Your Manufacturing Team’s Collaboration?</em></p>
<div>
<p>Want to learn more about how FlowFuse can enhance your industrial data operations and improve team collaboration?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Streamlining%20Node-RED%20Collaboration%20with%20FlowFuse">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/flowfuse-team-collaboration/#up-next"></a> Up Next</h2>
<p>Explore more resources and deepen your understanding of FlowFuse with these articles:</p>
<ul>
<li>
<p><strong><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/">Managing Node-RED Instances in a Centralized Platform</a></strong><br />
Learn how to effectively manage Node-RED instances in a centralized environment with FlowFuse, simplifying deployment and ensuring scalability.</p>
</li>
<li>
<p><strong><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/">Exploring FlowFuse Security Features</a></strong><br />
Discover the security tools and features built into FlowFuse to help protect your industrial data and keep your systems secure.</p>
</li>
<li>
<p><strong><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/">Building on FlowFuse: Working with Devices</a></strong><br />
Dive into how FlowFuse supports device management, enabling you to build, deploy, and monitor edge devices more efficiently.</p>
</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/Publishing Modbus data to your UNS with FlowFuseBridging Modbus Data to MQTT using FlowFuse2024-12-04T00:00:00ZSumit Shinde<p>As industries evolve towards Industry 4.0, integrating legacy systems like Modbus with modern cloud technologies becomes essential. While effective for local machine-to-machine communication, Modbus doesn't naturally connect with cloud platforms.</p>
<p>This article shows you how to bridge Modbus data to MQTT using FlowFuse and Node-RED. By publishing this data into a <a href="https://flowfuse.com/solutions/uns/">Unified Namespace (UNS)</a>, you can seamlessly integrate legacy systems with cloud services for real-time monitoring and analysis, unlocking the full potential of your industrial data.</p>
<!--more-->
<p>If you haven’t built a Unified Namespace yet, please read our article on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">building a UNS with FlowFuse</a> and create your own in just 15 minutes.</p>
<h2 id="the-importance-of-bridging-modbus-data-to-mqtt-for-cloud-integration-and-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#the-importance-of-bridging-modbus-data-to-mqtt-for-cloud-integration-and-uns"></a> The Importance of Bridging Modbus Data to MQTT for Cloud Integration and UNS</h2>
<p>Modbus has been a go-to protocol in industrial environments for a long time, providing reliable communication between devices like PLCs, sensors, and motors. It’s been essential for operational technology (OT) for decades. But as industries move towards Industry 4.0, there’s a growing need to connect older OT systems like Modbus with modern IT platforms, such as cloud systems and IoT applications. This is where the OT/IT gap becomes a challenge.</p>
<p>While Modbus does a great job at local control and data collection, it has some limitations when it comes to cloud integration and real-time IoT solutions. Without a centralized approach, data often remains locked in silos—isolated within different systems and devices. That’s where a Unified Namespace (UNS) comes in. A UNS acts as a bridge to break down these data silos. It’s a centralized architecture that allows different systems to communicate seamlessly, no matter the underlying technology. By setting up a UNS, you can easily bring in data from various systems, including Modbus, and consolidate it into one accessible point.</p>
<p>At FlowFuse, we support both Modbus and MQTT protocols, making it easy to integrate Modbus data into the UNS. We’ve chosen MQTT as the broker for our UNS because it’s lightweight, uses a pub-sub architecture, and is well-suited for real-time communication and cloud integration.</p>
<p>Once the Modbus data is in the UNS, it can be used for real-time monitoring, predictive maintenance, and analytics. For instance, in a factory still running Modbus with older systems, data from machines like motors, pumps, and conveyors can be sent to the UNS. This means stakeholders can access that data in real time, gaining insights into machine performance and system health. By consolidating data that was once trapped in silos into one place, the UNS helps organizations streamline operations, make better decisions, and boost efficiency.</p>
<h2 id="how-to-bridge-modbus-to-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#how-to-bridge-modbus-to-mqtt"></a> How to Bridge Modbus to MQTT</h2>
<p>Let's look at the steps to bridge Modbus data to MQTT using FlowFuse, leveraging Node-RED's capabilities. The process involves retrieving data from a Modbus device (For Practical example, we are using OpenSim to simulate Modbus data), transforming and processing the data (e.g., scaling raw sensor data into human-readable formats), and sending it to your Unified Namespace.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/bridging-modbus-data-to-mqtt-lbSxsv2n3R-1243.avif 1243w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/bridging-modbus-data-to-mqtt-lbSxsv2n3R-1243.webp 1243w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Bridging Modbus Data to MQTT using Node-RED" loading="lazy" decoding="async" src="https://flowfuse.com/img/bridging-modbus-data-to-mqtt-lbSxsv2n3R-1243.jpeg" width="1243" height="443" /></picture>
<em>Bridging Modbus Data to MQTT using Node-RED</em></p>
<h2 id="prequsite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#prequsite"></a> Prequsite</h2>
<ul>
<li>A <strong>Modbus data source</strong>, such as an actual Modbus device or a simulator like ModSim.</li>
<li>A <strong>FlowFuse account</strong>, which enables you to create and deploy Node-RED instances securely on the cloud with a single click, collaborate on projects with your team, manage and monitor edge devices centrally, handle data pipelines efficiently, and leverage its integrated MQTT broker service for seamless UNS management, with an intuitive interface for securely managing clients.</li>
</ul>
<p>If you haven’t signed up for a FlowFuse account yet, do so now by clicking <a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Publishing%20Modbus%20data%20to%20your%20UNS%20with%20FlowFuse">sign up</a>.</p>
<h3 id="step-1%3A-collect-data-from-modbus-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#step-1%3A-collect-data-from-modbus-devices"></a> Step 1: Collect Data from Modbus Devices</h3>
<p>The first step is to collect data from your Modbus devices. To do this, you'll need to run Node-RED on your Device. If your Modbus device communicates via a serial port, Node-RED will need access to that port, which you can manage with the appropriate configuration. If you're using Modbus TCP and both Node-RED and your Modbus device are on the same network, the connection is straightforward.</p>
<p><strong>Step 1.1: Running the FlowFuse device agent on your edge device</strong></p>
<p>To run Node-RED on your edge device with just a few simple steps, you can use the <a href="https://flowfuse.com/docs/device-agent/quickstart/">FlowFuse Device Agent</a>. This allows you to run Node-RED locally and also connect it to FlowFuse Cloud for remote monitoring and management, making it easier to keep track of your devices and workflows from anywhere.</p>
<p><strong>Step 1.2: Install Modbus Nodes</strong></p>
<p>While Node-RED doesn't include Modbus nodes by default, adding them to your palette is simple. Installing the necessary Modbus nodes from the Node-RED library will enable communication with your Modbus devices, whether they’re connected via serial or TCP. This step ensures you can start reading data from your Modbus devices and processing it for further integration.</p>
<ol>
<li>Open the <strong>Palette Manager</strong> by clicking the menu icon in the top-right corner of Node-RED and selecting <strong>Palette Manager</strong>.</li>
<li>In the Palette Manager, search for <code>node-red-contrib-modbus</code> in the search bar.</li>
<li>Click <strong>Install</strong> to add the Modbus nodes to your Node-RED environment.</li>
<li>Once installed, the nodes appear in the left-side palette under the <strong>Modbus</strong> category. These nodes will allow you to interact with Modbus devices in your flow.</li>
</ol>
<p><strong>Step 1.3: Configure the Modbus Connection</strong></p>
<p>Next, you'll need to configure the Modbus connection based on your device type. Modbus devices can communicate using two primary protocols: <strong>Modbus RTU</strong> (over serial) or <strong>Modbus TCP</strong> (over Ethernet/Wi-Fi). The specific choice depends on the type of Device you are working with.</p>
<ol>
<li>Drag a <strong>Modbus Read</strong> node onto your Node-RED Canvas.</li>
<li>Double-click on the <strong>Modbus Read</strong> node to open its configuration.</li>
<li>In the configuration window:
<ul>
<li>Enter the <strong>Unit ID</strong> (this is the device address, typically <strong>1</strong>, but it may vary depending on your device).</li>
<li>Choose the <strong>Function</strong> you need, such as <strong>Read Holding Registers</strong>, <strong>Read Input Registers</strong>, etc. (this depends on the type of data you want to read).</li>
<li>Specify the <strong>Start Address</strong> (the address of the first register you want to start reading).</li>
<li>Set the <strong>Quantity</strong> (the number of registers to read).</li>
<li>Specify the <strong>Poll Rate</strong> (e.g., how often you want to collect data, such as every 1 second).</li>
</ul>
</li>
<li>In the <strong>Server</strong> field, click the <strong>+</strong> button to add a new Modbus server, and select the type.
<ul>
<li>For <strong>Modbus TCP</strong>: Enter the <strong>IP address</strong> and <strong>Port</strong> (the default Modbus TCP port is <strong>502</strong>).</li>
<li>For <strong>Modbus RTU</strong>: If you're using a serial connection, you'll need to specify the serial port (such as <code>/dev/ttyUSB0</code> on Linux or <code>COM1</code> on Windows), as well as the baud rate and other serial settings.</li>
<li>Set the <strong>Unit ID</strong> (again, this should match the Unit ID you entered earlier).</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/modbus-connection-configuration-GrAoIiCNq6-723.avif 723w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/modbus-connection-configuration-GrAoIiCNq6-723.webp 723w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing Modbus node configuration for reading holding registers" loading="lazy" decoding="async" src="https://flowfuse.com/img/modbus-connection-configuration-GrAoIiCNq6-723.jpeg" width="723" height="733" /></picture>
<em>Image showing Modbus node configuration for reading holding registers</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/modbus-connection-config-weetmELtkW-720.avif 720w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/modbus-connection-config-weetmELtkW-720.webp 720w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing Modbus client node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/modbus-connection-config-weetmELtkW-720.jpeg" width="720" height="782" /></picture>
<em>Image showing Modbus client node configuration</em></p>
<ol start="5">
<li>Once the connection details are filled in, click <strong>Add</strong> to save the configuration, then click <strong>Done</strong>.</li>
</ol>
<p><strong>Step 1.4: Test the Modbus Connection</strong></p>
<p>After configuring the connection, it's time to test the data collection.</p>
<ol>
<li>Drag a <strong>Debug</strong> node onto the canvas.</li>
<li>Connect the <strong>Modbus Read</strong> node's output to to the input of <strong>Debug</strong> node.</li>
<li>Click <strong>Deploy</strong> in the top-right corner of Node-RED to deploy your flow.</li>
<li>Open the <strong>Debug Panel</strong> on the right side of the Node-RED interface. If the connection is successful, you should see the raw data from your Modbus device in the Debug Panel.</li>
</ol>
<p>If no data appears, check the connection settings (IP address, Unit ID, port, etc.) and ensure your Modbus device is correctly configured and accessible. If you use a simulator like ModSim, ensure it’s running and properly configured to send data.</p>
<p>For more information on using Modbus with Node-RED, please read our tutorial on <a href="https://flowfuse.com/node-red/protocol/modbus/">Using Modbus with Node-RED</a>.</p>
<h3 id="step-2%3A-transforming-modbus-data-for-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#step-2%3A-transforming-modbus-data-for-uns"></a> Step 2: Transforming Modbus Data for UNS</h3>
<p>After collecting data from your Modbus device, the next step is transforming it into a usable format for cloud-based IoT applications. Modbus data typically comes in raw register values, and you may need to convert these values into human-readable formats like temperature, pressure, or other measurements.</p>
<p>Let's walk through the transformation process step by step.</p>
<p><strong>Step 2.1: Parsing and Converting Raw Modbus Data</strong></p>
<p>Modbus devices often return data in registers that need to be interpreted. For example, a temperature sensor might return a register value like 350, which represents 35.0°C if the sensor stores values in tenths of degrees.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/modsim-gHWVyCrnx3-486.avif 486w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/modsim-gHWVyCrnx3-486.webp 486w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The ModSim interface, generating simulated Modbus data" loading="lazy" decoding="async" src="https://flowfuse.com/img/modsim-gHWVyCrnx3-486.jpeg" width="486" height="389" /></picture>
<em>The ModSim interface, generating simulated Modbus data</em></p>
<p>Here’s an example of the raw Modbus data I am receiving from ModSim: <code>[225, 1013, 29, 50, 603]</code>. These values represent the following:</p>
<ul>
<li><code>225</code>: Temperature (in tenths of degrees, which would be 22.5°C)</li>
<li><code>1013</code>: Part 1 of the pressure value (higher register)</li>
<li><code>29</code>: Part 2 of the pressure value (lower register)</li>
<li><code>50</code>: Vibration (in tenths of degrees, which would be 5g)</li>
<li><code>603</code>: Humidity (in tenths of degrees, which would be 60.3%)</li>
</ul>
<p>We must convert these raw register values into human-readable formats for cloud integration. For instance, we divide the temperature and vibration by 10 to get the actual values in degrees Celsius and g, respectively, and similarly for other parameters like humidity. For pressure, the higher and lower register values are combined to compute the complete value accurately.</p>
<p>To determine how to process raw Modbus data, such as dividing by a specific value, concatenating, or applying other transformation formulas, refer to the manual of the sensor you use for specific instructions.</p>
<p>In Node-RED, you can use various nodes for transformation. You can choose the <a href="https://flowfuse.com/node-red/core-nodes/function/">Function node</a> for advanced processing, the <a href="https://flowfuse.com/node-red/core-nodes/change/">Change node</a> for simpler operations, or the <a href="https://flowfuse.com/node-red/core-nodes/template/">Template node</a> for defining schemas. In this article, I will demonstrate a low-code approach using the Change node to process the data cleanly.</p>
<p>Additionally, for better organization and accessibility, I will send each metric separately and include additional metadata such as the <code>timestamp</code> and <code>unit</code>.</p>
<p><strong>For Temperature</strong>:</p>
<ol>
<li>Drag a <strong>Change</strong> node onto the canvas.</li>
<li>Double-click the node to open its configuration panel.</li>
<li>Set the following in the <strong>Set</strong> rules:
<ul>
<li>Set <code>msg.data</code> to <code>msg.payload</code>.</li>
<li>Set <code>msg.payload</code> to <code>{}</code>.</li>
<li>Set <code>msg.payload.timestamp</code> to the timestamp function of the Change node.</li>
<li>Set <code>msg.payload.value</code> to <code>data[0] / 10</code>.</li>
<li>Set <code>msg.payload.unit</code> to <code>'c'</code>.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/temperature-data-tranformation-gJmVDjfDg9-387.avif 387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/temperature-data-tranformation-gJmVDjfDg9-387.webp 387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the Change node rules transforming temperature data" loading="lazy" decoding="async" src="https://flowfuse.com/img/temperature-data-tranformation-gJmVDjfDg9-387.jpeg" width="387" height="549" /></picture>
<em>Image showing the Change node rules transforming temperature data</em></p>
<ol start="4">
<li>Click <strong>Done</strong> to save the configuration.</li>
<li>Connect the first output of the <strong>Modbus Read</strong> node to the input of this <strong>Change</strong> node.</li>
</ol>
<p><strong>For Pressure</strong>:</p>
<ol>
<li>Drag another <strong>Change</strong> node onto the canvas.</li>
<li>Double-click the node to open its configuration panel.</li>
<li>Set the following in the <strong>Set</strong> rules:
<ul>
<li>Set <code>msg.data</code> to <code>msg.payload</code>.</li>
<li>Set <code>msg.payload</code> to <code>{}</code>.</li>
<li>Set <code>msg.payload.timestamp</code> to the timestamp function of the Change node.</li>
<li>Set <code>msg.payload.value</code> to <code>$number($string(data[1]) & $string(data[2]))</code> as a JSONata expression.</li>
<li>Set <code>msg.payload.unit</code> to <code>'ppm'</code>.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pressure-data-transformation-m--vgEMjwO-388.avif 388w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/pressure-data-transformation-m--vgEMjwO-388.webp 388w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the Change node rules transforming pressure data" loading="lazy" decoding="async" src="https://flowfuse.com/img/pressure-data-transformation-m--vgEMjwO-388.jpeg" width="388" height="553" /></picture>
<em>Image showing the Change node rules transforming pressure data</em></p>
<ol start="4">
<li>Click <strong>Done</strong> to save the configuration.</li>
<li>Connect the first output of the <strong>Modbus Read</strong> node to the input of this <strong>Change</strong> node.</li>
</ol>
<p><strong>For Vibration</strong></p>
<ol>
<li>Drag another <strong>Change</strong> node onto the canvas.</li>
<li>Double-click the node to open its configuration panel.</li>
<li>Set the following in the <strong>Set</strong> rules:
<ul>
<li>Set <code>msg.data</code> to <code>msg.payload</code>.</li>
<li>Set <code>msg.payload</code> to <code>{}</code>.</li>
<li>Set <code>msg.payload.timestamp</code> to the timestamp function of the Change node.</li>
<li>Set <code>msg.payload.value</code> to <code>data[3] / 10</code> as a JSONata expression.</li>
<li>Set <code>msg.payload.unit</code> to <code>'g'</code>.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/vibration-data-transformation-PSUvHDaOqh-394.avif 394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/vibration-data-transformation-PSUvHDaOqh-394.webp 394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the Change node rules transforming vibration data" loading="lazy" decoding="async" src="https://flowfuse.com/img/vibration-data-transformation-PSUvHDaOqh-394.jpeg" width="394" height="539" /></picture>
<em>Image showing the Change node rules transforming vibration data</em></p>
<ol start="4">
<li>Click <strong>Done</strong> to save the configuration.</li>
<li>Connect the first output of the <strong>Modbus Read</strong> node to the input of this <strong>Change</strong> node.</li>
</ol>
<p><strong>For Humidity</strong></p>
<ol>
<li>Drag another <strong>Change</strong> node onto the canvas.</li>
<li>Double-click the node to open its configuration panel.</li>
<li>Set the following in the <strong>Set</strong> rules:
<ul>
<li>Set <code>msg.data</code> to <code>msg.payload</code>.</li>
<li>Set <code>msg.payload</code> to <code>{}</code>.</li>
<li>Set <code>msg.payload.timestamp</code> to the timestamp function of the Change node.</li>
<li>Set <code>msg.payload.value</code> to <code>data[4] / 10</code> as a JSONata expression.</li>
<li>Set <code>msg.payload.unit</code> to <code>'%'</code>.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/humidity-data-transformation-qiSZgxl_PS-394.avif 394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/humidity-data-transformation-qiSZgxl_PS-394.webp 394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the Change node rules transforming humidity data" loading="lazy" decoding="async" src="https://flowfuse.com/img/humidity-data-transformation-qiSZgxl_PS-394.jpeg" width="394" height="550" /></picture>
<em>Image showing the Change node rules transforming humidity data</em></p>
<ol start="4">
<li>Click <strong>Done</strong> to save the configuration.</li>
<li>Connect the first output of the <strong>Modbus Read</strong> node to the input of this <strong>Change</strong> node.</li>
</ol>
<p>Once you have configured and connected all the Change nodes, add a <strong>Debug</strong> node to each Change node's output to verify that the transformed data appears as expected. Deploy the flow, then check the output in the Debug Panel to ensure that each metric is correctly formatted with the appropriate timestamp, value, and unit.</p>
<h3 id="step-3%3A-send-data-to-an-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#step-3%3A-send-data-to-an-uns"></a> Step 3: Send Data to an UNS</h3>
<p>After transforming the Modbus data into a human-readable format, the next step is to send it to a Unified Namespace (UNS). MQTT is one of the most popular choices for implementing a UNS because it is lightweight, efficient, and designed for real-time data transmission. Its low bandwidth requirements, message queuing capabilities, and secure and reliable communication support make it ideal for industrial IoT applications.</p>
<p><strong>Step 3.1: Preparing MQTT Broker</strong></p>
<p>FlowFuse simplifies the setup process by providing an integrated MQTT broker service for teams and enterprise users. This built-in service eliminates the need to set up an external broker. To use the FlowFuse MQTT broker, you need to create MQTT clients on the platform. These clients are secured with username and password authentication, ensuring only authorized devices can publish or subscribe to topics. For more details about the MQTT broker service provided by FlowFuse, refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">MQTT Broker Service Announcement</a>.</p>
<p><strong>To create MQTT clients:</strong></p>
<ol>
<li>Navigate to the FlowFuse platform and click "Broker" in the left sidebar.</li>
<li>click the "Create Client" button in the newly opened interface.</li>
<li>Enter a username and password for the client.</li>
<li>Configure topic access control patterns if needed, specifying which topics the client can publish or subscribe to.</li>
<li>Click "Save" to create the client.</li>
<li>Once saved, copy the newly added client's Client ID from the list and save it for later use.</li>
</ol>
<p><strong>Step 3.2: Configure MQTT Nodes in Node-RED</strong></p>
<ol>
<li>In the Node-RED editor, drag the <strong>mqtt out</strong> node onto the canvas.</li>
<li>Double-click the node, then click the <strong>+</strong> icon next to the <strong>Server</strong> field to configure your broker details:
<ul>
<li>For FlowFuse MQTT, use <code>broker.flowfuse.cloud</code> as the server address.</li>
<li>Enter the <strong>Client ID</strong>, <strong>username</strong>, and <strong>password</strong> that you generated earlier.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-config-node-IyFIFidWIu-631.avif 631w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-config-node-IyFIFidWIu-631.webp 631w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image mqtt broker node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-config-node-IyFIFidWIu-631.jpeg" width="631" height="528" /></picture>
<em>Image mqtt broker node configuration</em></p>
<ol start="3">
<li>Click <strong>Add</strong> to save the configuration.</li>
<li>Select the appropriate <strong>QoS</strong> (Quality of Service) level based on your needs.</li>
<li>Enter the Topic for the MQTT message, such as <code>plant2/Area4/Cell2/DeviceA/temperature</code>. This naming convention organizes data by plant, area, cell, and Device, following the ISA-95 equipment hierarchy. It is widely used across industries because it makes managing, filtering, and scaling your system easier. Additionally, this approach simplifies data segmentation and provides straightforward access when other parts of the IoT ecosystem need it.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-client-config-temperature-KQxffwDJ6H-497.avif 497w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-client-config-temperature-KQxffwDJ6H-497.webp 497w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the MQTT Out node configuration " loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-client-config-temperature-KQxffwDJ6H-497.jpeg" width="497" height="364" /></picture>
<em>Image showing the MQTT Out node configuration</em></p>
<p><em>Note: For the configuration of MQTT nodes and Modbus, environment variables are used to prevent accidental exposure when sharing. For more information, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment Variables with Node-RED</a>.</em></p>
<ol start="6">
<li>Click <strong>Done</strong> to save the node.</li>
<li>Connect the output of the Change node, which is transforming the metrics you are sending via MQTT, to the mqtt out node.</li>
</ol>
<p>Repeat these steps for each metric (e.g., pressure, vibration, humidity), giving each one its own <strong>mqtt out</strong> node with a unique topic.</p>
<p>For more information on using MQTT with Node-RED, please read <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">Using MQTT with Node-RED</a>.</p>
<p>Once you’ve configured the MQTT nodes for all your metrics and deployed the flow, check the status at the bottom of each MQTT node. If it shows "connected," your Node-RED flow is successfully connected to the broker and publishes data to the UNS. From here, you can integrate the data with cloud-based analytics platforms. Build IoT dashboards with <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a>, or other systems to enable real-time monitoring, predictive maintenance, and automated decision-making. This setup effectively bridges the gap between legacy Modbus devices and modern IoT infrastructure, empowering smarter, more efficient industrial operations.</p>
<p>To view the topic hierarchy in UNS, go to the FlowFuse platform, navigate to the Broker section, and click on the Hierarchy tab. The interface will look similar to the one shown below:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.avif 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.webp 1782w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing FlowFuse topic heirachy intergace for UNS" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-mqtt-topic-hierarchy-monitoring-J_aeV92JSX-1782.jpeg" width="1782" height="858" /></picture>
<em>Image showing FlowFuse topic heirachy intergace for UNS</em></p>
<h2 id="final-thought" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/12/publishing-modbus-data-to-uns/#final-thought"></a> Final Thought</h2>
<p>Bridging Modbus data to MQTT and publishing it to a Unified Namespace (UNS) is crucial in modernizing industrial systems. With FlowFuse, you can easily connect legacy Modbus devices to the cloud, enabling real-time monitoring, analytics, and predictive maintenance.</p>
<p>FlowFuse offers a complete toolkit for building and managing your UNS. It combines MQTT for real-time communication, a robust data orchestration layer, and a centralized management platform for enterprise-level control. This unified approach simplifies the integration of devices, applications, and legacy systems, ensuring smooth data flow and delivering real-time insights across your organization.</p>
<p>With FlowFuse, you can optimize industrial data workflows, improve decision-making, and unlock the full potential of your operational data, all while reducing complexity and enhancing system scalability.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/Building a Unified Namespace (UNS) with FlowFuseImplement your Unified Namespace seamlessly using our low-code platform2024-11-28T00:00:00ZSumit Shinde<p>As systems and devices become more connected, managing data from different sources can be tricky. A <a href="https://flowfuse.com/solutions/uns/">Unified Namespace (UNS)</a> solves this by centralizing all your data in one place, making it easy to access and use.</p>
<!--more-->
<p><a href="https://flowfuse.com/">FlowFuse</a> makes building a UNS simple. It connects old and new systems, collects data from devices and applications, and streamlines workflows. With tools like Node-RED for data flow, MQTT for real-time updates, and a central management layer, FlowFuse helps you improve efficiency and make better decisions.</p>
<p>This article will show you how to build your UNS using FlowFuse, step by step.</p>
<h2 id="building-a-uns-with-real-time-sensor-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#building-a-uns-with-real-time-sensor-data"></a> Building a UNS with Real-Time Sensor Data</h2>
<p>This section explains how to set up a Unified Namespace (UNS) using FlowFuse, a Raspberry Pi, and an ADXL345 sensor. The Raspberry Pi collects data from the sensor, which we collect and process in Node-RED, calculate vibration magnitude, format it, and send it to the UNS using standardized topic names.</p>
<h3 id="step-1%3A-collect-metrics-from-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#step-1%3A-collect-metrics-from-devices"></a> Step 1: Collect Metrics from Devices</h3>
<p>The first step in building your UNS is collecting data from your devices. The method you choose will depend on the type of device and the communication protocol it supports. For example, many devices use traditional communication standards like Modbus, while others, such as industrial controllers, might rely on OPC-UA.</p>
<p>Fortunately, Node-RED provides support for a wide range of industrial protocols, from legacy to modern ones. While older protocols like Modbus and OPC-UA were originally designed for machine-to-machine (M2M) communication and are not directly compatible with cloud systems, Node-RED acts as a bridge to overcome this limitation.</p>
<p>By leveraging Node-RED, you can collect data from these legacy systems, process and transform the data using low-code workflows, and then seamlessly send it to the cloud via modern protocols such as MQTT, Kafka, AMQP, and more.</p>
<p>In our example, Node-RED can directly collect metrics from sensor using <a href="https://flows.nodered.org/node/node-red-contrib-i2c">I2C</a> on the Raspberry Pi. This approach simplifies the process by eliminating the need for additional communication layers. To run Node-RED on the Raspberry Pi, we use <a href="https://flowfuse.com/product/device-agent/">FlowFuse Device Agent</a>, This agent enables you to remotely monitor, manage, and build Node-RED flows securely through the FlowFuse platform remotely. <a href="https://flowfuse.com/node-red/hardware/">See here</a> for more details on how to set up and run FlowFuse Device Agent on different devices.</p>
<h3 id="step-2%3A-transform-and-process-the-collected-metrics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#step-2%3A-transform-and-process-the-collected-metrics"></a> Step 2: Transform and Process the Collected metrics</h3>
<p>Once you've collected data from your devices, the next step is to transform it using Node-RED. Industrial systems often use different protocols such as Modbus and OPC UA, and each might have its own data structure, which can create challenges for integration. For example, the ADXL345 sensor which we are uisng in our practile example outputs raw data as electrical signals (buffer data). We first need to format it into a human-readable format and then calculate the Magnitude, a standard vibration monitoring unit.</p>
<p><strong>Why Data Transformation Matters:</strong></p>
<ul>
<li><strong>Consistency</strong>: Ensures data from different sources follows the same structure.</li>
<li><strong>Integration</strong>: It makes integrating data from various systems easier.</li>
<li><strong>Speed</strong>: Simplifies data access for faster insights and decision-making.</li>
</ul>
<h4 id="example%3A-transforming-data-from-adxl345-sensor-(raspberry-pi)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#example%3A-transforming-data-from-adxl345-sensor-(raspberry-pi)"></a> Example: Transforming Data from ADXL345 Sensor (Raspberry Pi)</h4>
<p>Raw data from the ADXL345 sensor might look like this:</p>
<div style="position: relative" id="code-container-63">
<pre class="language-json"><code id="code-63" class="language-json"><span class="token punctuation">[</span><span class="token number">26</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">244</span><span class="token punctuation">,</span><span class="token number">255</span><span class="token punctuation">,</span><span class="token number">37</span><span class="token punctuation">,</span><span class="token number">255</span><span class="token punctuation">]</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-63" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Using a <a href="https://flowfuse.com/node-red/core-nodes/function/">Function node</a> in Node-RED, we can convert this into a human-readable format.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/function-node-AR7_Gd-QKe-1220.avif 1220w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/function-node-AR7_Gd-QKe-1220.webp 1220w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Function node: Transforming Raw Data into Readable Format" loading="lazy" decoding="async" src="https://flowfuse.com/img/function-node-AR7_Gd-QKe-1220.jpeg" width="1220" height="556" /></picture>
<em>Function node: Transforming Raw Data into Readable Format</em></p>
<p>After transformation, the data will look as shown below.</p>
<div style="position: relative" id="code-container-73">
<pre class="language-json"><code id="code-73" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"x"</span><span class="token operator">:</span><span class="token number">0.09765625</span><span class="token punctuation">,</span><br /> <span class="token property">"y"</span><span class="token operator">:</span><span class="token number">-0.046875</span><span class="token punctuation">,</span><br /> <span class="token property">"z"</span><span class="token operator">:</span><span class="token number">-0.8828125</span><br /><span class="token punctuation">}</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-73" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>While this is more readable, it can still be challenging to monitor changes in vibration quickly or detect anomalies. To simplify monitoring, we can calculate the Magnitude, a single metric commonly used in vibration monitoring.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-calculating-magnitude-i-0N5AWS9s-1416.avif 1416w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-calculating-magnitude-i-0N5AWS9s-1416.webp 1416w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Change node: Calculating Magnitude" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-calculating-magnitude-i-0N5AWS9s-1416.jpeg" width="1416" height="374" /></picture>
<em>Change node: Calculating Magnitude</em></p>
<p>After calculating the Magnitude using a <a href="https://flowfuse.com/node-red/core-nodes/change/">Change node</a>, the data might look like this:</p>
<div style="position: relative" id="code-container-83">
<pre class="language-json"><code id="code-83" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"x"</span><span class="token operator">:</span><span class="token number">0.00390625</span><span class="token punctuation">,</span><br /> <span class="token property">"y"</span><span class="token operator">:</span><span class="token number">-0.07421875</span><span class="token punctuation">,</span><br /> <span class="token property">"z"</span><span class="token operator">:</span><span class="token number">-0.8515625</span><span class="token punctuation">,</span><br /> <span class="token property">"magnitude"</span><span class="token operator">:</span><span class="token number">0.8547996098775871</span><br /><span class="token punctuation">}</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-83" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Now, the data is easier to monitor with a single metric (Magnitude), but this structure is still not optimal for a UNS. We need to transform it further to provide more context.</p>
<p><strong>Enhanced Data Structure for UNS:</strong></p>
<p>To make the data more useful for integration and interpretation in a UNS, we can transform it to include additional context, such as units and timestamps, while removing unnecessary metrics like the three-axis components (<code>x</code>, <code>y</code>, <code>z</code>). The easiest and most low-code approach for achieving this in Node-RED is to use the Change node, which is specifically designed for formatting and structuring payloads.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-structering-data-5nq2ohOd8A-618.avif 618w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-structering-data-5nq2ohOd8A-618.webp 618w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Change Node: Formatting and structuring payload for UNS" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-structering-data-5nq2ohOd8A-618.jpeg" width="618" height="648" /></picture>
<em>Change Node: Formatting and structuring payload for UNS</em></p>
<p>After formatting, the data will look as shown below:</p>
<div style="position: relative" id="code-container-99">
<pre class="language-json"><code id="code-99" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"vibration"</span><span class="token punctuation">,</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token string">"2024-11-13T10:00:00Z"</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"m/s²"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token number">0.8547996098775871</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-99" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This format is more structured and consistent, with important labels like <code>value</code>, <code>unit</code>, and <code>timestamp</code> that provide meaningful context. It clarifies that the value represents the Magnitude of vibration in <strong>m/s²</strong> and provides the precise time when the data was collected.</p>
<h3 id="step-3%3A-setting-up-your-uns-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#step-3%3A-setting-up-your-uns-broker"></a> Step 3: Setting Up Your UNS Broker</h3>
<p>Now, it's time to configure your UNS broker. As mentioned, we’ll use the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">FlowFuse MQTT Broker</a>. This broker is integrated within the FlowFuse platform to simplify your workflow by eliminating the need for multiple separate services. With FlowFuse, you can monitor and configure everything from a single, centralized platform. This ensures you can efficiently monitor, manage, and configure your UNS without juggling multiple tools or services.</p>
<p><strong>Steps to Set Up the FlowFuse MQTT Broker:</strong></p>
<ol>
<li>Log in to the FlowFuse platform and navigate to <strong>"Broker"</strong> in the left sidebar.</li>
<li>Click the <strong>"Create Client"</strong> button at the top-right corner to add a new MQTT client.</li>
<li><strong>Configure the client</strong>:
<ul>
<li>Provide a <strong>Username</strong> and <strong>Password</strong> for secure access.</li>
<li>Define an <strong>Access Pattern</strong> to manage client permissions.</li>
</ul>
</li>
<li>Click <strong>"Create"</strong> to generate the client.</li>
</ol>
<p><em>Note: Enterprise-level teams can register up to 20 clients, and teams-level teams can register up to 5 clients as part of their plan. The ability to purchase additional packs of clients will come in a near future release.</em></p>
<ol start="5">
<li>Copy the client ID you generated from the list and save it somewhere for later use.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/creating-client-interface-ZvHhVDOZ6O-1716.avif 1716w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/creating-client-interface-ZvHhVDOZ6O-1716.webp 1716w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Interface for creating MQTT Client" loading="lazy" decoding="async" src="https://flowfuse.com/img/creating-client-interface-ZvHhVDOZ6O-1716.jpeg" width="1716" height="936" /></picture>
<em>FlowFuse Interface for creating MQTT Client</em></p>
<h3 id="step-4%3A-choosing-your-topic-naming-convention" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#step-4%3A-choosing-your-topic-naming-convention"></a> Step 4: Choosing Your Topic Naming Convention</h3>
<p>The key to building a successful UNS is organizing your data with a clear and consistent naming convention. A well-designed convention ensures data is accessible and understandable across systems and users, simplifying communication and integration.</p>
<p><strong>ISA-95</strong> is a standard for industrial systems encompassing various manufacturing and communication aspects. However, when it comes to communication, ISA-95 often relies on point-to-point (P2P) connections between systems and devices. These connections can introduce complexity, delays, and other challenges.</p>
<p>While we are building a UNS to address the problems and limitations we observed with point-to-point communication, we can still leverage key elements of ISA-95 that remain valuable for improving production efficiency. One of the central aspects of ISA-95 is its equipment hierarchical model, which links various layers of a factory, from physical devices to enterprise systems. By adapting this model to your data architecture, you can simplify data access and management across the entire system.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.avif 1421w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.webp 1421w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="ISA-95 : Equipment Hierarchical Model" loading="lazy" decoding="async" src="https://flowfuse.com/img/isa-95-equipement-model-3B2qQjVLBn-1421.jpeg" width="1421" height="978" /></picture>
<em>ISA-95 : Equipment Hierarchical Model</em></p>
<p>For example, following an ISA-95-based equipment hierarchy to define your topic naming convention allows you to access data from devices, sensors, or any other source without knowing their specific addresses or tags—such as for a PLC. With clarity and ease, this logical structure enables you to retrieve relevant information from different system layers (e.g., from control systems to MES or ERP).</p>
<p>Example topic structure based on ISA-95 equipment model hierarchy:</p>
<p><code>Plant1/Area3/Line4/Cell2/DeviceA</code></p>
<p><code>Plant1/Area4/Line5/Cell6/DeviceB</code></p>
<p>You can also use the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/"><strong>Sparkplug B</strong></a> naming convention for MQTT topics, which offers a structured hierarchy and standard. However, the Sparkplug B convention has some limitations in terms of flexibility. A typical <strong>Sparkplug B topic</strong> follows this structure:</p>
<p><code>spBv1.0/{groupID}/{edgeNodeID}/{deviceID}/{messageType}</code></p>
<p>While Sparkplug B provides a standardized topic model, its hierarchy may not always suit the specific needs of your architecture. Alternative methods, such as the Paris and Schultz models, help address these limitations with Sparkplug B topics. However, to keep things simple and avoid unnecessary complexity, we will use plain MQTT with the ISA-95 hierarchy.</p>
<h3 id="step-5%3A-sending-collected-metrics-to-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#step-5%3A-sending-collected-metrics-to-uns"></a> Step 5: Sending Collected metrics to UNS</h3>
<p>With your topic naming convention chosen, it’s time to send the data to the UNS. In Node-RED, we will use the <a href="https://flowfuse.com/node-red/core-nodes/mqtt/">MQTT Out</a> node to send the transformed data to the broker.</p>
<ol>
<li>Drag an MQTT out node into your flow.</li>
<li>Configure the node to connect to the FlowFuse MQTT Broker using the client credentials generated earlier.</li>
</ol>
<p><strong>Note</strong>: Use environment variables to secure configuration and prevent exposing credentials when sharing flows. This ensures that sensitive data remains secure and allows easy sharing without compromising security. For more details, refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Article: Using Environment Variables in Node-RED</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-node-config-vYv46QDmTF-1028.avif 1028w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-node-config-vYv46QDmTF-1028.webp 1028w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring mqtt-broker-config node" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-node-config-vYv46QDmTF-1028.jpeg" width="1028" height="856" /></picture>
<em>Configuring mqtt-broker-config node</em></p>
<p>{data-zoomable}
<em>Configuring mqtt-broker-config node</em></p>
<ol start="3">
<li>Set the <strong>topic</strong> using your predefined naming convention (e.g., <code>Plant1/Area3/Line4/Cell2/RPI1</code>).</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-out-node-config-CMIM3pHgf_-820.avif 820w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-out-node-config-CMIM3pHgf_-820.webp 820w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring mqtt-out node" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-out-node-config-CMIM3pHgf_-820.jpeg" width="820" height="660" /></picture>
<em>Configuring mqtt-out node</em></p>
<ol start="4">
<li>Connect the input of the mqtt out node to your data transformation flow and deploy the flow.</li>
</ol>
<p>For information on how to use MQTT with Node-RED, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">Using MQTT with Node-RED</a></p>
<p>After deploying, you can monitor the topic hierarchy on the FlowFuse platform by switching to the "Hierarchy" tab in the Broker interface.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/topic-hierarchy-pV87i0KLyO-1444.avif 1444w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/topic-hierarchy-pV87i0KLyO-1444.webp 1444w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Monitoring your mqtt topic hierarchy within FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/topic-hierarchy-pV87i0KLyO-1444.jpeg" width="1444" height="534" /></picture>
<em>Monitoring your mqtt topic hierarchy within FlowFuse</em></p>
<p>Once your data is in the UNS, you have a centralized, real-time view of your operations. This unified structure enables easier access, sharing, and analysis of data across systems, helping you drive better decisions, improve efficiency, and gain valuable insights to optimize your processes.</p>
<p>With real-time data access, you can create monitoring dashboards using the <a href="https://flowfuse.com/product/dashboard/">FlowFuse Dashboard</a> with a low-code approach, integrate with other cloud solutions, or leverage it further for enhanced analytics or automation.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-dashboard-EcorH_koch-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-dashboard-EcorH_koch-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Dashboard Monitoring Vibrations" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-dashboard-EcorH_koch-1920.jpeg" width="1920" height="695" /></picture>
<em>FlowFuse Dashboard Monitoring Vibrations</em></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/#conclusion"></a> Conclusion</h2>
<p>FlowFuse makes building a Unified Namespace (UNS) easy by centralizing data from systems, devices, and sensors. With seamless integration of Node-RED, MQTT, and its enterprise layer, FlowFuse ensures smooth data flow, real-time insights, and efficient management. It simplifies operations, enhances productivity, and improves system interoperability, making it easy to adapt your UNS to specific needs and support real-time analytics and automation.</p>
<div>
<p>Curious about how FlowFuse can simplify building your Unified Namespace?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=Building%20a%20Unified%20Namespace%20%28UNS%29%20with%20FlowFuse">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/The Death of Point-to-Point: Why You Need a Unified NamespaceTraditional Point-to-Point Connection Model is Holding Back Manufacturing Innovation - Unified Namespace (UNS) Can Transform Factory Operations2024-11-26T00:00:00ZSumit Shinde<p>Manufacturing has changed significantly over the years, driven by new technology and the need for better communication between systems. In the past, point-to-point (P2P) connections, where devices communicate directly with each other, were the standard. However, as factories become more complex, P2P connections are no longer practical. This article explains why P2P connections are outdated and how a Unified Namespace (UNS) offers a better, more flexible solution.</p>
<!--more-->
<h2 id="what-are-point-to-point-connections%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#what-are-point-to-point-connections%3F"></a> What are Point-to-Point Connections?</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/p2p-WF9QFMJ6vq-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/p2p-WF9QFMJ6vq-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Point-to-point connection" loading="lazy" decoding="async" src="https://flowfuse.com/img/p2p-WF9QFMJ6vq-1920.jpeg" width="1920" height="977" /></picture>
<em>Point-to-point connection</em></p>
<p>Point-to-point (P2P) connections are direct links between two systems or devices, allowing them to communicate with each other. They can be physical, like cables, or network-based (client-server), like a machine sending data to a server.</p>
<p>In a P2P setup, each connection links exactly two systems. These systems could be hardware, software, or even databases, exchanging data tailored to their needs.</p>
<p>For example, a machine might send performance data directly to a control system, or a sensor could send real-time measurements to a monitoring device. It’s a straightforward way to get systems talking, but as the number of devices grows, it becomes increasingly complex to manage.</p>
<h2 id="why-point-to-point-connections-no-longer-work" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#why-point-to-point-connections-no-longer-work"></a> Why Point-to-Point Connections No Longer Work</h2>
<p>As manufacturing moves toward more modern, interconnected approaches like Industry 4.0, the limitations of point-to-point connections become more apparent. Here's why P2P connections are no longer sufficient for today’s manufacturing environments:</p>
<h3 id="1.-limited-data-sharing%2C-visibility%2C-and-delayed-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#1.-limited-data-sharing%2C-visibility%2C-and-delayed-data"></a> 1. Limited Data Sharing, Visibility, and Delayed Data</h3>
<p>Point-to-point connections often lead to data silos, where devices or machines communicate only with their immediate hierarchy level. This isolation severely limits system-wide visibility, making it challenging to share critical data in real-time. Consequently, problems such as defective products, unexpected downtime, and the need for rework may go unnoticed until the damage is done.</p>
<p>In modern manufacturing, real-time data is indispensable for maintaining operational efficiency. However, point-to-point connections introduce significant delays, as data must traverse multiple layers—control systems (Level 1), supervisory control (Level 2), manufacturing execution systems (Level 3), and finally, higher-level management systems (Level 4) as outlined in the ISA-95 model. Each additional layer compounds latency, slowing response times and postponing the detection of issues, see also our post on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/">Why the Automation Pyramid slows you down</a>.</p>
<p>Without integrated, real-time data across teams, such as quality control, problems like defective parts may not be addressed promptly, leading to increased waste and customer dissatisfaction. The lack of seamless data sharing results in visibility gaps that impede decision-making, reducing the ability to act swiftly. In fast-paced environments, these delays not only hinder operational efficiency but also have a direct negative impact on profitability.</p>
<h3 id="2.-inflexibility%2C-limiting-innovation%2C-and-causing-downtime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#2.-inflexibility%2C-limiting-innovation%2C-and-causing-downtime"></a> 2. Inflexibility, Limiting Innovation, and Causing Downtime</h3>
<p>Point-to-point connections create a rigid and inflexible network. When new technology or equipment is added, the entire system often requires reconfiguration, leading to significant downtime. For instance, if a new machine is introduced to the production line, many connections may need to be adjusted or re-established, which can temporarily halt production.</p>
<p>This downtime disrupts the flow of operations and makes it harder to implement new technologies quickly, slowing down innovation. As a result, manufacturers may struggle to stay competitive, as they can't integrate advancements like automation, real-time analytics, or AI without significant delays and costly interruptions.</p>
<h3 id="3.-high-costs-over-time-and-maintenance-complexity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#3.-high-costs-over-time-and-maintenance-complexity"></a> 3. High Costs Over Time and Maintenance Complexity</h3>
<p>At first glance, point-to-point (P2P) connections may appear to be a cost-effective and straightforward solution. However, as systems expand, the true costs and complexities become increasingly apparent. In the automation pyramid (ISA-95 model), communication occurs across multiple hierarchical levels, with field devices interacting with controllers, controllers with SCADA, and SCADA with higher-level systems such as MES and ERP.</p>
<p>While this layered structure can keep connections orderly, it quickly becomes a logistical nightmare when scaling. Introducing new devices often triggers a cascade of necessary updates and reconfigurations across various levels. For example, adding a new field device typically requires adjustments to the controller, followed by updates to the SCADA system, creating a ripple effect that impacts the entire system.</p>
<p>This ongoing need for constant reconfiguration not only drives up costs but also introduces significant complexity in maintenance. As the system grows, so does the effort and resources required to manage it, making the P2P model inefficient and prohibitively expensive in environments that demand agility, scalability, and long-term sustainability. Ultimately, the simplicity of P2P connections gives way to an increasingly cumbersome and expensive maintenance burden, undermining its initial advantages.</p>
<h3 id="4.-security-vulnerabilities" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#4.-security-vulnerabilities"></a> 4. Security Vulnerabilities</h3>
<p>As the number of point-to-point connections increases, so does the risk of security breaches. Each connection represents a potential vulnerability, and if one connection in the network is compromised, the entire system becomes a target for attackers.</p>
<p>In large manufacturing environments, securing every individual connection becomes daunting. Any new device or system can introduce additional vulnerabilities, creating more opportunities for attackers to exploit. A compromised point-to-point connection could lead to production halts, loss of sensitive data, or even physical damage to machinery.</p>
<p>For example, imagine a situation where a hacker gains access to a machine’s control system through a compromised point-to-point connection. The attacker could intentionally cause a malfunction, halt production, or extract confidential information. The complexity of managing security for each connection makes it difficult to maintain a secure, reliable network.</p>
<h3 id="5.-scalability-issues" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#5.-scalability-issues"></a> 5. Scalability Issues</h3>
<p>One of the most significant drawbacks of P2P connections in manufacturing is their inability to scale efficiently. As production lines grow, so too do the number of devices, systems, and connections that must be managed. With a P2P architecture, each new device or system typically requires a direct, dedicated connection to each relevant part of the network. This creates a spaghetti network of interwoven links that becomes increasingly unwieldy and difficult to manage as the system expands.</p>
<p>In a traditional P2P setup, scaling the network means manually creating additional links, configuring them, and ensuring that the new connections fit seamlessly into the existing infrastructure. This process is time-consuming, error-prone, and highly resource-intensive, leading to increased complexity and longer downtimes as you scale.</p>
<p>When scaling a P2P network, changes made to one part of the system often trigger a ripple effect throughout the entire network. For instance, adding a new sensor may require updating the controller, the SCADA system, and even the Manufacturing Execution System (MES). This cascading need for updates across different layers of the network makes scaling more complicated and costly. Furthermore, the introduction of new devices means additional configuration and troubleshooting, often leading to disruptions in operations and extended downtimes while the new devices are integrated.</p>
<p>These issues compound when scaling across multiple production lines or sites, creating an increasingly complex web of p2p connections. As the number of devices grows, so does the risk of errors, network failures, and delays. This makes it difficult for manufacturers to respond to the growing demands of production while maintaining efficiency, reliability, and uptime.</p>
<h2 id="unified-namespace%3A-the-modern-solution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#unified-namespace%3A-the-modern-solution"></a> Unified Namespace: The Modern Solution</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/hub--j9P9_ZYf9-712.avif 712w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/hub--j9P9_ZYf9-712.webp 712w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img width="400px" data-zoomable="" alt="Hub and Spoke Model" loading="lazy" decoding="async" src="https://flowfuse.com/img/hub--j9P9_ZYf9-712.jpeg" height="727" /></picture>
<em>Hub and Spoke Model</em></p>
<p>A Unified Namespace (UNS) is a more straightforward way to connect devices and systems in a factory. Instead of having separate connections between each device, everything is connected through one central hub, which we call the <strong>hub-and-spoke</strong> model. This means devices don’t need to be directly linked to each other, making the system easier to manage and maintain; for more information on Unified Namespace, read our article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/">Introduction to unified namespace</a>.</p>
<p>With a UNS, adding new devices or systems becomes straightforward. Rather than setting up multiple direct connections—like in a point-to-point system—new devices (spokes) connect to the central hub. This reduces the complexity of growing your system and eliminates downtime. If equipment is replaced or updated, only that device needs to be reconnected to the hub rather than reconfiguring the entire network.</p>
<p>A UNS also improves data sharing. All data is collected in one place, so any system that needs it can access it in real time. This leads to quicker decisions and faster responses to problems. With fewer connections to manage, the costs of maintaining the system are lower. Plus, the central hub makes the whole system more secure, as fewer direct connections need to be protected.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.avif 1231w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.webp 1231w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Publish-Subscribe Archtecture" loading="lazy" decoding="async" src="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.jpeg" width="1231" height="553" /></picture>
<em>Publish-Subscribe Archtecture</em></p>
<p>We use a <strong>publish-subscribe (pub/sub)</strong> architecture to implement a hub-and-spoke model in a UNS. In this architecture, devices, systems send data to a central broker, and other devices can subscribe to the data they need. This approach eliminates the need for a complex network of point-to-point connections, making it easier to scale, update, and maintain the system.</p>
<p>This model addresses all the significant problems of point-to-point connections. For more information on how pub/sub solves these problems or why UNS needs pub/sub, read the article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/">Why UNS Needs Pub/Sub</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">MQTT</a> is a widely used for implementing the publish-subscribe model. It is lightweight, efficient, and works well in manufacturing environments where network reliability can be inconsistent.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr-in-uns-HH1QAJj9Rj-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr-in-uns-HH1QAJj9Rj-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Unified Namespace" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr-in-uns-HH1QAJj9Rj-1920.jpeg" width="1920" height="735" /></picture>
<em>Unified Namespace</em></p>
<p>To simplify the creation of a UNS in your manufacturing environment, <a href="https://flowfuse.com/">FlowFuse</a> provides an integrated MQTT broker service. The platform makes building, scaling, and managing Node-RED solutions easy. It supports seamless connections between devices, services, and APIs using over 5,000 community-contributed nodes for data collection. FlowFuse also enables efficient data transformation and visualization with a low-code approach, remote management of edge devices, and team collaboration on projects. With everything centralized on one platform, FlowFuse offers high security, scalability, and availability to optimize and maintain your system effectively.</p>
<p>Get started with this article <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">Building UNS with FlowFuse</a>.</p>
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-point-to-point-connection-is-dead/#summary"></a> Summary</h2>
<p>Due to complexity, high maintenance costs, and security risks, point-to-point (P2P) connections are becoming less effective in modern factories. A Unified Namespace (UNS) solves these problems by connecting devices through a central hub, making managing, scaling, and securing systems easier. UNS improves data sharing and reduces downtime. Tools like FlowFuse make it simple to set up and manage a UNS, offering a more efficient and flexible solution for manufacturing.</p>
<div>
<p>Start building your own Unified Namespace with FlowFuse and take the first step toward a more efficient and connected manufacturing system.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.cloud/account/create/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=The%20Death%20of%20Point-to-Point%3A%20Why%20You%20Need%20a%20Unified%20Namespace">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/introducing-industrial-visionaries-podcast/Introducing the Industrial Visionaries Podcast!Introducing Industrial Visionaries, the podcast that explores the minds behind the industry's biggest breakthroughs.2024-11-26T00:00:00Z<p>Today, we’re excited to announce our new podcast: Industrial Visionaries!</p>
<!--more-->
<p>This show is dedicated to exploring the transformative power of technology in the manufacturing sector.</p>
<p>In each episode, our host ZJ van de Weg, CEO of FlowFuse, brings you face-to-face with industry leaders who are pioneering digital solutions, sharing insights that will help you navigate the future of manufacturing.</p>
<h3 id="episode-1%3A-flowfuse%E2%80%99s-nick-o%E2%80%99leary-on-low-code-development-and-its-impact-on-iot" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/introducing-industrial-visionaries-podcast/#episode-1%3A-flowfuse%E2%80%99s-nick-o%E2%80%99leary-on-low-code-development-and-its-impact-on-iot"></a> Episode 1: FlowFuse’s Nick O’Leary on Low-Code Development and Its Impact on IoT</h3>
<p>In our inaugural episode of Industrial Visionaries, our host ZJ van de Weg, chats with fellow FlowFuse exec, Nick O’Leary, CTO & Founder. Nick, who is also the creator of Node-RED, shares his extensive insights into the evolution of the Internet of Things (IoT). He discusses the early days of IoT and the transformative role of edge computing, highlighting how increased computing power has shifted data processing closer to devices.</p>
<p>Listen to the first episode on:</p>
<ul>
<li><a href="https://podcasts.apple.com/us/podcast/ep-1-flowfuses-nick-oleary-on-low-code-development/id1781774461?i=1000678217258">Apple Podcasts</a></li>
<li><a href="https://open.spotify.com/episode/6HJB35FbK1U7pVNpTyM6P2">Spotify</a></li>
<li><a href="https://www.youtube.com/watch?v=AI-bjry8vLU">YouTube</a></li>
</ul>
<p>We look forward to bringing you more conversations with actionable insights that help in your pursuit to innovate in your industry.</p>
<p>For the latest episodes, search for “Industrial Visionaries” on Spotify, Apple, YouTube, or wherever you listen to podcasts!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/Getting the Most Out of MQTT for Industrial IoTBest Practices for Optimizing MQTT in Industrial IoT Systems2024-11-25T00:00:00ZSumit Shinde<p>MQTT is a go-to protocol for industrial IoT, known for its efficiency, scalability, and ease of use. While it offers great flexibility in handling real-time data, there are key factors to consider in order to get the most out of it. From ensuring data consistency to addressing security and performance concerns, these factors can significantly enhance MQTT’s effectiveness in industrial settings.</p>
<!--more-->
<p>This post dives into how to optimize MQTT for industrial IoT, covering best practices and key considerations. It also highlights how FlowFuse can help streamline MQTT communication, improving data reliability, security, and integration across devices and systems.</p>
<h2 id="standardizing-data-formats-for-better-quality-and-consistency" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#standardizing-data-formats-for-better-quality-and-consistency"></a> Standardizing Data Formats for Better Quality and Consistency</h2>
<p>Industrial data is generated by a wide variety of devices, machines, and systems, making it highly valuable. However, to fully leverage this data, it needs to be standardized and formatted in a way that ensures seamless processing across different systems, especially since it can come in a variety of formats.</p>
<p>One of MQTT’s key strengths is its flexibility in handling various data formats. While this is an advantage, it can sometimes require additional effort to ensure that the data is structured consistently and in a way that’s easy to use. Standardizing data formats helps organizations streamline processes, making data easier to interpret, analyze, and act upon. Without a consistent approach, data inconsistencies may arise, slowing down insights or causing compatibility issues.</p>
<p>Frameworks like <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/">Sparkplug B</a> are designed to help address this by providing a standardized way to handle MQTT payloads. By adopting such frameworks, companies can ensure that data is delivered in a consistent, well-structured format, improving compatibility and making it easier to analyze. This not only simplifies integration across diverse systems but also enables more reliable decision-making and efficient operations.</p>
<h2 id="optimizing-payloads-and-bandwidth-for-efficient-mqtt-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#optimizing-payloads-and-bandwidth-for-efficient-mqtt-communication"></a> Optimizing Payloads and Bandwidth for Efficient MQTT Communication</h2>
<p>MQTT is designed for lightweight, efficient messaging, which makes it ideal for many IIoT applications. However, in cases where large amounts of data or high-frequency messages need to be transmitted, bandwidth constraints can sometimes impact performance.</p>
<p>While the MQTT protocol supports large message payloads (up to 250 MB), transmitting large files such as high-resolution images or detailed sensor data can slow down communication. Fortunately, there are simple yet effective strategies to keep your MQTT network running smoothly, even when dealing with sizable payloads.</p>
<p>To optimize performance, consider compressing your data before sending it, which reduces the overall size of the payload and makes transmission faster and more efficient. For even larger files, such as video or binary data, a great approach is to store these files externally (e.g., in cloud storage like AWS S3) and send only a reference or pointer to the data. This avoids the need to transmit large files over MQTT while ensuring data accessibility.</p>
<p>By using these strategies, you can optimize MQTT performance and ensure that your system runs efficiently, even when handling large or high-frequency data. These techniques allow MQTT to continue providing fast, reliable, and scalable communication, regardless of the data size or frequency.</p>
<h2 id="ensuring-reliable-data-delivery-with-mqtt%E2%80%99s-qos-levels" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#ensuring-reliable-data-delivery-with-mqtt%E2%80%99s-qos-levels"></a> Ensuring Reliable Data Delivery with MQTT’s QoS Levels</h2>
<p>In industrial environments, the accuracy and timeliness of data transmission are crucial for maintaining operational efficiency and safety. MQTT’s publish-subscribe model is designed for lightweight, real-time messaging, but it’s important to understand how to leverage its features to ensure the reliability and integrity of the data flow.</p>
<p>While the protocol is generally robust, in some cases, challenges such as network disruptions or temporary communication failures can occur. These can lead to issues such as message loss or delivery out of order. Fortunately, MQTT provides built-in mechanisms to address these challenges and ensure that messages reach their intended destination reliably.</p>
<p>One of the key feature for managing message delivery is Quality of Service (QoS), which defines how MQTT handles message delivery:</p>
<ul>
<li><strong>QoS 0</strong>: Messages are delivered at most once, with no guarantee of delivery or order. This level is ideal for scenarios where occasional message loss is acceptable and minimizing network overhead is critical.</li>
<li><strong>QoS 1</strong>: Guarantees that messages are delivered at least once, although duplicates may occur. This is useful when data consistency is important, but some duplication can be handled by the system.</li>
<li><strong>QoS 2</strong>: Ensures messages are delivered exactly once and in the correct order. This highest level of QoS is suitable for scenarios where data integrity and sequencing are paramount, such as in robotic control systems or safety-critical operations.</li>
</ul>
<p>By choosing the right QoS level, you can align MQTT’s behavior with the operational needs of your system, balancing between performance, data reliability, and system resources. For real-time applications, a lower QoS level may be sufficient, while in more critical situations, the higher QoS levels offer a stronger guarantee of data delivery and integrity.</p>
<p>With these options, MQTT allows you to fine-tune message delivery to meet the specific requirements of your industrial IoT environment, ensuring that data flows smoothly and reliably, even in challenging network conditions.</p>
<h2 id="implementing-acknowledgment-mechanisms-in-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#implementing-acknowledgment-mechanisms-in-mqtt"></a> Implementing Acknowledgment Mechanisms in MQTT</h2>
<p>One important consideration when working with MQTT is the lack of built-in acknowledgment mechanisms. Unlike traditional request-response communication models, where the receiver explicitly confirms the receipt of a message, MQTT doesn't natively offer a direct way for the receiver to acknowledge successful message receipt.</p>
<p>This lack of visibility can pose challenges in scenarios where it’s crucial to ensure that data has been transmitted successfully and processed as expected. In some industrial systems, failure to confirm receipt of messages could lead to operational uncertainty or potential errors.</p>
<p>However, while MQTT does not provide this feature out-of-the-box, it is possible to implement acknowledgment mechanisms to enhance reliability. we can use a separate topic where the receiver sends a confirmation message (such as “ack” or “received”) back to the sender, indicating that the message was successfully processed.</p>
<p>This allow you to implement acknowledgment functionality, but they do require additional planning and development effort. Ensuring that these mechanisms are correctly designed and integrated can improve system reliability by providing the necessary feedback loop for confirming message receipt and processing.</p>
<p>By incorporating acknowledgment systems, you can enhance the visibility and confidence in data transmission, ensuring that your industrial IoT system operates smoothly and that no critical messages are missed or lost.</p>
<h2 id="optimizing-mqtt-for-resource-constrained-environments" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#optimizing-mqtt-for-resource-constrained-environments"></a> Optimizing MQTT for Resource-Constrained Environments</h2>
<p>While MQTT’s use of TCP/IP ensures reliable message delivery, it can introduce challenges in environments where devices have limited resources. Maintaining persistent TCP connections can consume significant processing power and memory, which may be a concern for low-power or resource-constrained devices.</p>
<p>To address this, some IoT systems use MQTT-SN (MQTT for Sensor Networks), a variant that operates over UDP and is specifically designed for devices with limited resources. MQTT-SN reduces the overhead associated with maintaining a TCP connection, making it a better fit for battery-powered or embedded devices that need to conserve energy. However, it's important to note that MQTT-SN is distinct from the standard MQTT specification, as it involves different message formats and communication mechanisms.</p>
<p>By carefully selecting the appropriate protocol and architecture for your specific use case, you can continue to leverage MQTT-like messaging capabilities, while optimizing performance in environments with limited resources.</p>
<h2 id="ensuring-security-in-mqtt-deployments" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#ensuring-security-in-mqtt-deployments"></a> Ensuring Security in MQTT Deployments</h2>
<p>Security is essential when using MQTT in industrial environments. While MQTT offers powerful features like encryption and authentication, it's crucial to configure these elements correctly to maximize security. A properly secured MQTT deployment ensures that your system remains protected from risks such as data interception, unauthorized access, or man-in-the-middle attacks, while ensuring that your communication remains secure and reliable.</p>
<p>To begin, implementing encryption (SSL/TLS) is an important step to protect the data transmitted between devices and brokers. This ensures that sensitive information is kept confidential and is shielded from unauthorized access. By enabling encryption, you create a secure communication pathway that prevents eavesdropping and data tampering.</p>
<p>Next, consider authentication for all devices and users. By using mechanisms such as usernames, passwords, or client certificates, you can ensure that only authorized devices and users can connect to your MQTT broker. This access control mechanism ensures that only the right people and devices have access to the network, safeguarding it from potential threats.</p>
<p>Topic-level access control is another key aspect of securing your MQTT deployment. By managing who can publish or subscribe to specific topics, you can prevent unauthorized access to sensitive data or critical commands. For example, you can restrict which devices or users are allowed to subscribe to specific topics that control industrial equipment or monitor sensitive processes. This level of control ensures that only trusted entities can interact with certain parts of your system, reducing the risk of malicious actions.</p>
<p>In addition to restricting access to topics, it's also important to regularly monitor your MQTT traffic for any unusual activity or anomalies. By staying vigilant and analyzing traffic patterns, you can identify potential security threats early and take action to prevent any disruptions.</p>
<p>By configuring your MQTT setup with encryption, authentication, topic-level access control, and ongoing monitoring, you can create a secure, reliable, and scalable IoT system. A well-secured MQTT deployment not only reduces risks but also enhances the efficiency and safety of industrial operations, providing peace of mind as your system grows.</p>
<h2 id="minimizing-risks-of-single-point-of-failure-and-vendor-lock-in" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#minimizing-risks-of-single-point-of-failure-and-vendor-lock-in"></a> Minimizing Risks of Single Point of Failure and Vendor Lock-In</h2>
<p>When using MQTT for industrial operations, it’s important to address the potential risks associated with a single point of failure—specifically the central broker. While the broker is crucial for managing message delivery, its failure could disrupt the entire data flow, impacting operations. However, this challenge can be effectively managed with strategies like high availability, load balancing, and robust backup solutions. By implementing these best practices, you ensure that your system remains reliable and resilient, even in the event of a failure.</p>
<p>Another consideration is vendor lock-in, which can occur when businesses become reliant on a specific MQTT broker or service. While this may seem convenient at first, it can make future changes or migrations difficult due to proprietary features or configurations that aren't easily compatible with other systems. This is a scenario that can be easily avoided with careful planning.</p>
<p>Although MQTT is an open standard, some cloud services may not fully adhere to the MQTT 3.1.1 specification. This can sometimes lead companies to rely on proprietary software development kits (SDKs) for sending MQTT messages. While these tools can work well initially, they can limit your ability to switch vendors or integrate with other systems down the road.</p>
<p>To maintain flexibility and avoid vendor lock-in, it's crucial to choose an MQTT broker that fully supports MQTT's open standards. Brokers that follow standards like MQTT 3.1.1 or MQTT 5.0 ensure compatibility and interoperability, making it easier to switch providers or integrate new technologies as your needs evolve.</p>
<h2 id="how-flowfuse-enhances-your-industrial-iot-system" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#how-flowfuse-enhances-your-industrial-iot-system"></a> How FlowFuse Enhances Your Industrial IoT System</h2>
<p>FlowFuse is an industrial data platform that streamlines the management, scaling, and security of IoT applications. Built on Node-RED at its core, FlowFuse offers seamless integration with various industrial protocols, including MQTT, ensuring reliable communication between devices. With its robust feature set, FlowFuse makes it easier to build secure, scalable, and efficient IoT solutions for industrial environments</p>
<p>A key strength of FlowFuse is its ability to standardize data before sending it to the MQTT broker. Using Node-RED’s intuitive low-code programming capabilities, you can define consistent data formats and topics across all connected devices. This ensures smooth integration between diverse systems and maintains data consistency, allowing everything to work together seamlessly. FlowFuse also supports frameworks like <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/">Sparkplug B</a>, providing additional structure for more reliable communication with MQTT payloads.</p>
<p>Security is another area where FlowFuse excels. It allows you to configure MQTT nodes with SSL/TLS encryption, username/password authentication, and other security measures to ensure secure communication between devices. This protects sensitive data from unauthorized access and ensures that your system’s communications remain confidential.</p>
<p>FlowFuse also simplifies the creation of acknowledgment mechanisms. With Node-RED, you can design custom workflows to track message receipt and processing, ensuring data integrity and improving operational transparency. This level of control guarantees that your system operates smoothly and reliably.</p>
<p>Additionally, FlowFuse provides its own MQTT broker service that follows open standards, helping you avoid vendor lock-in and offering the flexibility to scale and adapt as your needs evolve. With built-in high availability, load balancing, and access control mechanisms, FlowFuse ensures continuous and reliable and secure data flow. And you can scale its capabilities by contacting FlowFuse support to meet the growing demands of your industrial IoT system.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.cloud/account/create/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Getting%20the%20Most%20Out%20of%20MQTT%20for%20Industrial%20IoT">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/getting-the-most-out-of-mqtt-for-industrial-iot/#conclusion"></a> Conclusion</h2>
<p>Getting the most out of MQTT for industrial IoT is all about smart strategies and the right tools. By focusing on data consistency, security, and performance, you can build a resilient and efficient IoT ecosystem. With FlowFuse, you get a powerful, flexible platform that not only streamlines MQTT communication but also helps you stay ahead of challenges like security risks and vendor lock-in. Whether you're optimizing payloads, enhancing security, or scaling your system, FlowFuse makes it easier to unlock the full potential of your industrial IoT operations.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/FlowFuse 2.11: MQTT Topic Hierarchy, UI Revamp & Improved LoggingLet's take a look at the new features and improvements in FlowFuse 2.112024-11-21T00:00:00ZJoe Pavitt<p>The focus of the FlowFuse 2.11 release has all been about providing clarity for our users and reducing friction in our user experience.</p>
<p>Navigation in FlowFuse has been streamlined with a new sidebar and team-wide search feature. We've provided an interactive visualization for your MQTT topic hierarchy, ensuring you have a clear view of your own MQTT/UNS architecture, and, we've re-architected the audit logging to ensure you have an easy-to-understand and searchable view of everything going on in your FlowFuse Team and it's respective Applications, Instances and Devices.</p>
<!--more-->
<h2 id="navigation-revamp" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#navigation-revamp"></a> Navigation Revamp</h2>
<p>If you're using FlowFuse Cloud, you'll have already noticed a big improvement to our navigation sidebar on the left of the user interface that was released a couple of weeks ago. This is now packaged up into FlowFuse 2.11 and available to our self-hosted customers too.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-sidebar-PJ4B5h4XcF-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-sidebar-PJ4B5h4XcF-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the new navigation sidebar in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-sidebar-PJ4B5h4XcF-1920.jpeg" width="1920" height="1201" /></picture>
<em>Screenshot of the improve left-side navigation in FlowFuse</em></p>
<p>This is the first stage of a navigation revamp we're working on, that will represent a big improvement for navigation around FlowFuse, and help make managing your applications easier.</p>
<p>You'll notice we've renamed "Devices" to "Edge Devices" to make it clearer that these are <em>instances of Node-RED</em> that are running on devices, rather than just a record of the device itself. We've also separated out many of the pages into sections, making it easier to find what you're looking for, and giving us scope to add in more in the coming weeks, which will make it easy to jump straight to the features you need.</p>
<h3 id="team-wide-search" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#team-wide-search"></a> Team-wide Search</h3>
<p>A very popular feature in the "home" view of FlowFuse is the search bar that helps you find the relevant Application, Instance, or Device you're looking for. This has been moved into the top header and will be available on every page of FlowFuse, making it even easier to find what you're looking for, wherever you are.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-search-WESyalTEo0-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-search-WESyalTEo0-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the team-wide search" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-search-WESyalTEo0-1920.jpeg" width="1920" height="1213" /></picture>
<em>Screenshot of the team-wide search</em></p>
<p>This will soon be followed by an update to our "Applications" view which is currently quite over-crowded. We're always looking to reduce friction for our users, and this is a big step in doing that.</p>
<h2 id="mqtt-topic-hierarchy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#mqtt-topic-hierarchy"></a> MQTT Topic Hierarchy</h2>
<p>We recently <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">announced our very own MQTT Service</a>, and we're following that up with an update that lets you see what topics are being used by your MQTT clients in the UI:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-mqtt-hierarchy-IqoVF75pT7-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-mqtt-hierarchy-IqoVF75pT7-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the UI to explore your MQTT topic hierarchy" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-mqtt-hierarchy-IqoVF75pT7-1920.jpeg" width="1920" height="1279" /></picture>
<em>Screenshot of the UI to explore your MQTT topic hierarchy</em></p>
<p>This will make managing your event-driven applications even easier, giving you clarity on the structure of your topic-space, whether you're using the MQTT Broker for a unified namespace (UNS) or any other use case.</p>
<h2 id="audit-logging-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#audit-logging-improvements"></a> Audit Logging Improvements</h2>
<p>We've improved the Audit Log views at the Team and Application levels to given you better visibility on the actions taken by users across your whole team in FlowFuse.</p>
<p>Previously, only events associated to that "level" (e.g. Team, Application, Instance) were shown in the respective log. However, now, when you view the "Team" Audit Log, it shows not just events on that team specifically, e.g. a settings change, but also all Audit events for it's "children", i.e. the Applications, Instances, Pipelines, etc. that are part of that team too.</p>
<p>New filters on the right-side also make it easy to explore everything taking place in your FlowFuse Team, allowing you to dive into a given Instance, all from the top-level "Team" view.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/audit-log-child-events-YmJyJM62jv-1262.avif 1262w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/audit-log-child-events-YmJyJM62jv-1262.webp 1262w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the improved audit log view" loading="lazy" decoding="async" src="https://flowfuse.com/img/audit-log-child-events-YmJyJM62jv-1262.jpeg" width="1262" height="839" /></picture>
<em>Screenshot of the improved audit log view</em></p>
<p>For a full list of everything that went into our 2.11 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.11.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.10, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<p>If you have an Enterprise license please make sure to review this <a href="https://flowfuse.com/changelog/2024/08/enterprise-license-update/">changelog entry</a></p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/flowfuse-release-2-11/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/Why UNS needs Pub/SubDiscover How Pub/Sub Transforms Unified Namespace into a Scalable, Real-Time Data Powerhouse for Modern Manufacturing.2024-11-19T00:00:00ZSumit Shinde<p>As the manufacturing industry evolves and becomes increasingly connected through the Industrial Internet of Things (IIoT), the concept of a Unified Namespace (UNS) has emerged as a critical architecture for centralizing and organizing data. UNS serves as a central reference point where all operational data, from machines to the enterprise, can be accessed in a consistent and structured way. Over time, more and more manufacturers have adopted UNS to simplify data integration and improve real-time visibility across systems.</p>
<p>If you're unfamiliar with UNS, please read our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/">Introduction to Unified Namespace</a> for a basic understanding of UNS.</p>
<!--more-->
<p>If you're familiar with or already using a UNS, you might be asking: <strong>Why does a UNS need Pub/Sub</strong>? Here, we’ll explore how combining the Pub/Sub model with a Unified Namespace can help manufacturers streamline data flow, reduce latency, and enable more responsive and scalable operations.</p>
<h2 id="what-is-pub%2Fsub%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#what-is-pub%2Fsub%3F"></a> What is Pub/Sub?</h2>
<p>Before discussing why Publish/Subscribe (Pub/Sub) is essential for a UNS, let's first define the Pub/Sub model.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.avif 1231w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.webp 1231w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Publish Subscribe Model" loading="lazy" decoding="async" src="https://flowfuse.com/img/pub-sub-HVZSAZdyjG-1231.jpeg" width="1231" height="553" /></picture>
<em>Publish Subscribe Model</em></p>
<p>The Pub/Sub model is a way for systems to communicate where one component, called the publisher, sends messages to a central system ( Broker such as <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">MQTT</a>, <a href="https://flowfuse.com/node-red/protocol/amqp/">RabitMQ</a>, and <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/">Kafka</a> ), and other components, called subscribers, receive those messages. The publisher doesn’t need to know who the subscribers are, and the subscribers don’t know who the publishers are. The central system, or broker, ensures the right messages go to the right subscribers based on their interests.</p>
<p>Additionally, it’s important to note that the roles of publisher and subscriber are not mutually exclusive. A component can act as a publisher in one context, sending messages to the broker, and as a subscriber in another, receiving messages from the broker.</p>
<h2 id="why-does-a-uns-need-pub%2Fsub%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#why-does-a-uns-need-pub%2Fsub%3F"></a> Why Does a UNS Need Pub/Sub?</h2>
<p>Now that you have a basic understanding of Pub/Sub, let’s dive into why this architecture is essential for a Unified Namespace.</p>
<h3 id="decoupling-producers-and-consumers-with-flexible-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#decoupling-producers-and-consumers-with-flexible-communication"></a> Decoupling Producers and Consumers with Flexible Communication</h3>
<p>In traditional manufacturing setups, various systems and machines often operate in silos, requiring point-to-point connections for communication. This means each device or system must be directly linked to others, which quickly becomes complex and cumbersome as the number of systems grows. Point-to-point integrations are hard to scale because each new device needs a dedicated connection. This not only makes data harder to access but also blocks innovation by creating dependencies between systems.</p>
<p>To learn more about how point-to-point systems restrict innovation, please read the article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/">Why the Automation Pyramid Blocks Digital Transformation</a></p>
<p>The Pub/Sub model provides a more scalable solution. In this model, publishers (such as IoT devices or machines) generate data and send it to a central system. Subscribers (other systems, applications, or users) then receive only the data relevant to them. Crucially, the publishers and subscribers do not need to know about each other, relying instead on a central Namespace to distribute the data efficiently.</p>
<p>By using Pub/Sub in a UNS, industries can create a single reference point for all data, enabling systems to subscribe to the data streams they need, without direct connections to every other system. For example, a production line monitoring system can easily access data from temperature sensors, pressure gauges, and robotic arms without the need for complex point-to-point integrations.</p>
<p>Unlike point-to-point systems, the Pub/Sub model allows for flexible communication—whether one-to-one, one-to-many, or many-to-many—without the need for direct connections between each system. This flexibility ensures that as your factory evolves, new devices and applications can easily be integrated into the UNS, driving innovation.</p>
<h3 id="event-driven-and-asynchronous-communication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#event-driven-and-asynchronous-communication"></a> Event-driven and Asynchronous Communication</h3>
<p>The Pub/Sub model is particularly well-suited for event-driven architectures, where systems react to changes in data rather than periodically polling for updates. This is important in environments where responsiveness is key—such as in predictive maintenance, supply chain optimization, or automated decision-making.</p>
<p>For instance, a predictive maintenance system can subscribe to real-time sensor data from machines in a UNS. When the system detects an anomaly (e.g., a machine vibration out of normal parameters), it can immediately trigger an alert or maintenance action. This type of asynchronous communication is more efficient and scalable than synchronous polling or direct communication between producers and consumers.</p>
<h3 id="less-delay%2C-more-efficiency" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#less-delay%2C-more-efficiency"></a> Less Delay, More Efficiency</h3>
<p>In industries where IIoT-enabled operations are prevalent, real-time data is essential for effective decision-making. Traditional systems often introduce delays due to multiple layers of data collection, storage, and processing. These delays can result in inefficiencies such as slow machine adjustments, missed production targets, or equipment failures.</p>
<p>The Pub/Sub model reduces latency by immediately pushing data to subscribers as soon as it’s available. There’s no need for systems to poll or wait for periodic updates. Instead, they can respond to real-time events as soon as they occur.</p>
<p>For instance, in a smart factory, if a machine’s temperature exceeds a threshold, a maintenance system could instantly react by scheduling a technician, triggering an alert, or even pausing operations. Without Pub/Sub, the system would have to rely on polling mechanisms, which are less efficient and often introduce unnecessary delays.</p>
<p>In real-time environments, these immediate actions can make the difference between preventing costly downtime or catching a problem too late.</p>
<h3 id="easy-to-scale-as-you-grow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#easy-to-scale-as-you-grow"></a> Easy to Scale as You Grow</h3>
<p>Manufacturers are always expanding—whether by adding more machines to the production line, introducing new automation systems, or scaling up to handle more products or more data. Scaling traditional systems to keep up with this growth can be complex and costly, especially when new devices or technologies need to be integrated.</p>
<p>The beauty of Pub/Sub is that it scales effortlessly. When new machines or sensors are introduced, they simply publish their data to the namespace. No complex reconfiguration or integration is required. Similarly, if a new application needs to access this data, it can simply subscribe to the relevant streams.</p>
<p>For example, consider a car manufacturer adding new robotic arms to the production line. These robots can publish real-time performance data, such as arm movement speeds, energy consumption, and fault alerts, directly into the UNS. The factory’s existing data systems can then subscribe to this new data without requiring changes to the entire system, making integration quick and cost-effective.</p>
<p>This level of scalability helps manufacturers keep up with growth without worrying about complex system upgrades or slowdowns.</p>
<h3 id="making-your-systems-more-reliable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#making-your-systems-more-reliable"></a> Making Your Systems More Reliable</h3>
<p>In manufacturing, system downtime is costly. However traditional, monolithic systems often rely on point-to-point connections, which can create vulnerabilities. If one part of the system goes down, it can bring down other systems or halt production entirely.</p>
<p>With a Pub/Sub architecture, this is less of a concern. If one publisher (like a sensor or machine) fails or goes offline, the rest of the systems can continue operating as normal. Other sensors or machines can continue to send their data, and subscribers can still receive real-time updates from other sources.</p>
<p>Consider a delivery system in a Point-to-Point setup, all vehicles send data directly to a central dispatch system. If the central dispatch system fails, data from all vehicles is lost. In a Pub/Sub setup, vehicles send their data to a central namespace instead of directly to the dispatch system. If the central dispatch system fails, it doesn’t result in data loss. Vehicles can still send their updates.</p>
<p>This decoupling of systems improves resilience, meaning that your factory can continue running smoothly even if individual components experience issues.</p>
<p>While the broker is a critical part of the Pub/Sub system and can also go down, this risk can be mitigated. Strategies such as high-availability brokers, failover mechanisms, and clustered deployments can be implemented to prevent downtime and ensure uninterrupted data flow.</p>
<h3 id="taking-action" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#taking-action"></a> Taking Action</h3>
<p>As we've discussed, the Pub/Sub model is a game-changer for Unified Namespace architectures in the manufacturing industry. One of the most widely adopted protocols for implementing Pub/Sub is MQTT (Message Queuing Telemetry Transport). MQTT is known for its simplicity, efficiency, and low-bandwidth requirements, making it an ideal choice for Industrial applications.</p>
<p>To help you leverage the power of MQTT in your manufacturing operations along with Node-RED, Flowfuse offers a robust MQTT broker service. Now, Flowfuse will not only help you build, scale, and manage Node-RED solutions, collaborate across teams, and manage edge devices, but it will also simplify your integration of IIoT data streams into your Unified Namespace. For more information on how our FlowFuse MQTT broker Service, read our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20mqtt%20announcement&utm_term=high_intent&utm_content=Why%20UNS%20needs%20Pub%2FSub/">MQTT Broker Service Announcement</a>.</p>
<p>Get started with this article <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/building-uns-with-flowfuse/">Building UNS with FlowFuse</a>.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/why-pub-sub-in-uns/#conclusion"></a> Conclusion</h2>
<p>Integrating Pub/Sub with a Unified Namespace enhances manufacturing operations by enabling real-time data flow, reducing latency, and improving scalability. This combination ensures efficient, resilient, and future-ready systems, empowering manufacturers to stay competitive in the IIoT era.</p>
<div>
<p>Curious about how FlowFuse can simplify building your Unified Namespace?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Why%20UNS%20needs%20Pub%2FSub">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/Interacting with ESP32 Using Node-RED and MQTTBuilding IoT Flows with ESP32 and FlowFuse2024-11-14T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<iframe width="100%" height="315" src="https://www.youtube.com/embed/ecfJ-9MxyVE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#prerequisites"></a> Prerequisites</h2>
<p>To follow this tutorial, you'll need the following:</p>
<ul>
<li><strong>ESP32 microcontroller</strong>: The hardware you'll be using for this project.</li>
<li><strong>USB cable</strong>: To connect the ESP32 to your computer.</li>
<li><strong>Arduino IDE</strong>: Installed and set up to program your ESP32. <a href="https://support.arduino.cc/hc/en-us/articles/360019833020-Download-and-install-Arduino-IDE">Download</a> the Arduino IDE if you haven't already done so.
<ul>
<li>Additionally, if you haven't set up the Arduino IDE for the ESP32 board, please follow this tutorial: <a href="https://www.youtube.com/watch?v=CD8VJl27n94">How to Set Up ESP32 with Arduino IDE</a></li>
</ul>
</li>
<li><strong>FlowFuse account</strong>: 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.</li>
</ul>
<p>If you haven’t signed up for a FlowFuse account yet, <a href="https://app.flowfuse.com/account/create/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Interacting%20with%20ESP32%20Using%20Node-RED%20and%20MQTT">sign up</a> now.</p>
<h2 id="getting-started-with-esp32-and-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#getting-started-with-esp32-and-node-red"></a> Getting Started with ESP32 and Node-RED</h2>
<p>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.</p>
<h3 id="step-1%3A-creating-node-red-instance-on-flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#step-1%3A-creating-node-red-instance-on-flowfuse-cloud"></a> Step 1: Creating Node-RED instance on FlowFuse Cloud</h3>
<p>Start by logging into your <a href="https://flowfuse.com/">FlowFuse</a> account and creating a new Node-RED instance. For more information on creating a Node-RED instance, refer to the <a href="https://flowfuse.com/docs/user/introduction/#creating-a-node-red-instance">FlowFuse documentation</a>.</p>
<p>Once the instance is created, open the Node-RED editor.</p>
<h3 id="step-2%3A-creating-and-configuring-mqtt-clients-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#step-2%3A-creating-and-configuring-mqtt-clients-in-flowfuse"></a> Step 2: Creating and Configuring MQTT Clients in FlowFuse</h3>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/esp32-mqtt-node-red-4PxKvPoiS2-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/esp32-mqtt-node-red-4PxKvPoiS2-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Diagram showing the flow of data and how commands are sent to the ESP32 using MQTT using Node-RED." loading="lazy" decoding="async" src="https://flowfuse.com/img/esp32-mqtt-node-red-4PxKvPoiS2-1920.jpeg" width="1920" height="545" /></picture>
<em>Diagram showing the flow of data and how commands are sent to the ESP32 using MQTT using Node-RED</em></p>
<p>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.</p>
<p><strong>To Create MQTT Clients in FlowFuse:</strong></p>
<ol>
<li>Navigate to your FlowFuse platform and log in to your account.</li>
<li>In the left sidebar, click on "Broker".</li>
<li>In the newly opened interface, click the “Create Client” button.</li>
<li>Enter a username and password for your MQTT client. Confirm the password.
<ul>
<li>You can leave the default pattern as <code>#</code> for access control, or set a custom pattern if needed.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-client-create-SR0ecthrWF-1292.avif 1292w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-client-create-SR0ecthrWF-1292.webp 1292w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Interface for setting MQTT client details and credentials" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-client-create-SR0ecthrWF-1292.jpeg" width="1292" height="1028" /></picture>
<em>Interface for setting MQTT client details and credentials</em></p>
<ol start="5">
<li>Click "Create" to generate the client.</li>
<li>Copy the client ID and save it somewhere secure for later use.</li>
<li>Repeat the same steps to create the second MQTT client for the ESP32.</li>
</ol>
<h3 id="step-3%3A-building-a-node-red-dashboard-to-send-commands-over-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#step-3%3A-building-a-node-red-dashboard-to-send-commands-over-mqtt"></a> Step 3: Building a Node-RED Dashboard to Send Commands Over MQTT</h3>
<p>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.</p>
<p><strong>Let's first create a flow to connect to the MQTT broker with the client config we have created:</strong></p>
<ol>
<li>Drag the <strong>mqtt out</strong> node onto the canvas in Node-RED.</li>
<li>Double-click the <strong>mqtt out</strong> node to open the settings.</li>
<li>Click the pencil icon next to the Server field to open the MQTT broker configuration.</li>
<li>In the configuration, enter the following details:
<ul>
<li>Server: <code>broker.flowfuse.cloud</code></li>
<li>Client ID: The Client ID you created earlier.</li>
<li>Username: The MQTT username (Client ID).</li>
<li>Password: The MQTT password.</li>
</ul>
</li>
<li>Click "Add" to save the configuration, then select the newly added configuration.</li>
<li>In the Topic field, enter a topic name, such as <code>/LEDControl</code>.</li>
<li>Click "Done" to close the settings.</li>
<li>Click "Deploy" in the top-right corner to deploy the flow.</li>
<li>Once deployed, check the MQTT out node for a Connected status, confirming the connection to the MQTT broker.</li>
</ol>
<p>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: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">FlowFuse Dashboard: Getting Started</a></p>
<ol>
<li>Install the <code>@flowfuse/node-red-dashboard</code> from the Node-RED Palette Manager.</li>
<li>Drag two <strong>ui-button</strong> widgets onto the canvas.</li>
<li>Double-click on the first button and set the Label to "ON", the Background Color to Green, and the Payload to <code>1</code>. Adjust the Width and Height as needed.</li>
<li>Double-click on the second button and set the Label to "OFF", the Background Color to Red, and the Payload to <code>2</code>.</li>
<li>Connect the output of both buttons to the input of the <strong>mqtt out</strong> node.</li>
<li>Click "Deploy" to save the flow.</li>
</ol>
<div id="nr-flow-161" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow161 = "\n[{\"id\":\"59887a8115c95eae\",\"type\":\"tab\",\"label\":\"Flow 1\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"02c25e8a30f9379d\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"appIcon\":\"\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\":\"/page1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"cfb2ab9ff30660fc\",\"breakpoints\":[{\"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\":\"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\":\"/LedControl\",\"qos\":\"\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"def97b29f5f7baab\",\"x\":390,\"y\":140,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow161.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-161') })</script>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard2-hdC8R71xlY-1386.avif 1386w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard2-hdC8R71xlY-1386.webp 1386w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Dashboard Build to control the ESP32 LED" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard2-hdC8R71xlY-1386.jpeg" width="1386" height="748" /></picture>
<em>FlowFuse Dashboard Build to control the ESP32 LED</em></p>
<h3 id="step-4%3A-programming-esp32-to-receive-commands-from-mqtt-and-control-led" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#step-4%3A-programming-esp32-to-receive-commands-from-mqtt-and-control-led"></a> Step 4: Programming ESP32 to receive commands from MQTT and Control LED</h3>
<p>Now, let's move on to the final step. Before proceeding, make sure your ESP32 is <strong>connected to your laptop or computer via USB</strong>*. 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.
The ESP32 will subscribe to the MQTT topic we configured earlier (e.g., /LEDControl). Based on the received payload (1 or 2), it will control the LED accordingly — turning it on or off.</p>
<p><strong>Setting up Arduino IDE:</strong></p>
<ol>
<li>Open the Arduino IDE on your computer.</li>
<li>Ensure you have selected the correct board and port in the Tools menu.</li>
<li>Install the necessary library:</li>
<li>Go to Tools > "Manage Libraries".</li>
<li>Search for and install the "EspMQTTClient" library by Patrick Lapointe.</li>
<li>The library installation will prompt you to install its dependencies—ensure that you tick that option and proceed to install.</li>
</ol>
<p><strong>Code for ESP32:</strong></p>
<ol>
<li>Copy the following code into the Arduino IDE:</li>
</ol>
<div style="position: relative" id="code-container-296">
<pre class="language-cpp"><code id="code-296" class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">if</span> <span class="token expression"><span class="token function">defined</span><span class="token punctuation">(</span>ESP32<span class="token punctuation">)</span></span></span><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><WiFi.h></span></span><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">elif</span> <span class="token expression"><span class="token function">defined</span><span class="token punctuation">(</span>ESP8266<span class="token punctuation">)</span></span></span><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><ESP8266WiFi.h></span></span><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">endif</span></span><br /><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><PubSubClient.h></span></span><br /><br /><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">define</span> <span class="token macro-name">LedPin</span> <span class="token expression"><span class="token number">2</span> </span><span class="token comment">// ESP32 built-in LED pin</span></span><br /><br /><span class="token comment">// WiFi and MQTT settings</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> ssid <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// Change this to your WiFi SSID</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> password <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// Change this to your WiFi password</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> mqtt_server <span class="token operator">=</span> <span class="token string">"broker.flowfuse.cloud"</span><span class="token punctuation">;</span> <span class="token comment">// FlowFuse MQTT broker server</span><br /><br /><span class="token comment">// MQTT client credentials</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> mqtt_client_id <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// Replace with your MQTT client ID</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> mqtt_username <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// Replace with your MQTT username</span><br /><span class="token keyword">const</span> <span class="token keyword">char</span><span class="token operator">*</span> mqtt_password <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token comment">// Replace with your MQTT password</span><br /><br />WiFiClient espClient<span class="token punctuation">;</span><br />PubSubClient <span class="token function">client</span><span class="token punctuation">(</span>espClient<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Function to connect to WiFi</span><br /><span class="token keyword">void</span> <span class="token function">setup_wifi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Connecting to "</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>ssid<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> WiFi<span class="token punctuation">.</span><span class="token function">begin</span><span class="token punctuation">(</span>ssid<span class="token punctuation">,</span> password<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">while</span><span class="token punctuation">(</span>WiFi<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> WL_CONNECTED<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"\nWiFi connected"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"IP address: "</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>WiFi<span class="token punctuation">.</span><span class="token function">localIP</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Callback function to handle messages from subscribed topics</span><br /><span class="token keyword">void</span> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">char</span><span class="token operator">*</span> topic<span class="token punctuation">,</span> byte<span class="token operator">*</span> payload<span class="token punctuation">,</span> <span class="token keyword">unsigned</span> <span class="token keyword">int</span> length<span class="token punctuation">)</span> <span class="token punctuation">{</span><br /><br /> String msg<span class="token punctuation">;</span><br /> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> msg <span class="token operator">+=</span> <span class="token punctuation">(</span><span class="token keyword">char</span><span class="token punctuation">)</span>payload<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// Control LED based on message</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>msg <span class="token operator">==</span> <span class="token string">"1"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">digitalWrite</span><span class="token punctuation">(</span>LedPin<span class="token punctuation">,</span> HIGH<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Turn LED on</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>msg <span class="token operator">==</span> <span class="token string">"2"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">digitalWrite</span><span class="token punctuation">(</span>LedPin<span class="token punctuation">,</span> LOW<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Turn LED off</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token comment">// Function to connect to MQTT broker</span><br /><span class="token keyword">void</span> <span class="token function">reconnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>client<span class="token punctuation">.</span><span class="token function">connected</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Attempting MQTT connection..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token comment">// Connect to MQTT broker with the client ID, username, and password</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>client<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span>mqtt_client_id<span class="token punctuation">,</span> mqtt_username<span class="token punctuation">,</span> mqtt_password<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Connected to MQTT broker"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> client<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token string">"/LedControl"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token keyword">else</span> <span class="token punctuation">{</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"Failed, rc="</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span>client<span class="token punctuation">.</span><span class="token function">state</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> Serial<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">" trying again in 5 seconds"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">delay</span><span class="token punctuation">(</span><span class="token number">5000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">void</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> Serial<span class="token punctuation">.</span><span class="token function">begin</span><span class="token punctuation">(</span><span class="token number">115200</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">pinMode</span><span class="token punctuation">(</span>LedPin<span class="token punctuation">,</span> OUTPUT<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token function">setup_wifi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> client<span class="token punctuation">.</span><span class="token function">setServer</span><span class="token punctuation">(</span>mqtt_server<span class="token punctuation">,</span> <span class="token number">1883</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> client<span class="token punctuation">.</span><span class="token function">setCallback</span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><br /><span class="token keyword">void</span> <span class="token function">loop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>client<span class="token punctuation">.</span><span class="token function">connected</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token function">reconnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> client<span class="token punctuation">.</span><span class="token function">loop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-296" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>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).</li>
<li>After you've made these changes, click "Upload" in the Arduino IDE to upload the code to your ESP32.</li>
<li>Once the upload is complete, open the Serial Monitor (set the baud rate to 115200) to monitor the output.</li>
</ol>
<p>If everything is set up correctly, you should see the output in the Serial Monitor as shown in the image.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/serial-monitor-pN-kj_vkUE-441.avif 441w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/serial-monitor-pN-kj_vkUE-441.webp 441w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Serial monitor displaying the result when everything is set up correctly." loading="lazy" decoding="async" src="https://flowfuse.com/img/serial-monitor-pN-kj_vkUE-441.jpeg" width="441" height="297" /></picture>
<em>Serial monitor displays the result when everything is set up correctly.</em></p>
<p>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.</p>
<h3 id="troubleshooting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#troubleshooting"></a> Troubleshooting</h3>
<ol>
<li>
<p><strong>Can't Upload Code to ESP32</strong></p>
<ul>
<li>Solution: Make sure the correct board and port are selected in the Arduino IDE.
Check Tools > Board for the right ESP32 model and Tools > Port for the correct connection.
If the port is missing, <a href="https://www.silabs.com/developer-tools/usb-to-uart-bridge-vcp-drivers?tab=downloads">Download</a> and reinstall the CP210x USB drivers.</li>
</ul>
</li>
<li>
<p><strong>ESP32 Keeps Disconnecting from MQTT</strong></p>
</li>
</ol>
<ul>
<li>Solution: Make sure both the ESP32 and Node-RED have unique MQTT client IDs.
If both devices share the same client ID, they will conflict and cause disconnections.</li>
</ul>
<ol start="3">
<li><strong>ESP32 Doesn’t Respond to Commands (LED Not Turning On/Off)</strong>
<ul>
<li>Solution: Verify the topic in the ESP32 code matches the one in Node-RED (e.g., /LedControl). If it still doesn't work, try rebooting your ESP32.</li>
</ul>
</li>
</ol>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/esp32-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/Migrating from Self-Managed Node-RED to FlowFuse-Managed Node-REDA Step-by-Step Guide to Transitioning Your Node-RED Flows to a Streamlined FlowFuse Environment2024-11-13T00:00:00ZSumit Shinde<p>Migrating your Node-RED instance to <a href="https://flowfuse.com/">FlowFuse</a> centralizes management and simplifies deployment. Once migrated, FlowFuse takes care of the infrastructure, security, and scalability, making the process much easier. This allows you to focus on building solutions without worrying about the complexities of self-hosting. Whether you're working with edge devices or want to work on cloud instances, this migration streamlines the management of your IIoT workflows, improving efficiency and scalability.</p>
<!--more-->
<p>Let's explore how to migrate from a self-managed and self-hosted Node-RED setup to a FlowFuse-managed environment. We'll look at how the migration works for both edge devices and cloud instances.</p>
<h2 id="why-switch-from-self-managed-node-red-to-flowfuse-managed-node-red%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#why-switch-from-self-managed-node-red-to-flowfuse-managed-node-red%3F"></a> Why Switch from Self-Managed Node-RED to FlowFuse-Managed Node-RED?</h2>
<p>Managing self-hosted Node-RED instances can introduce a range of challenges, especially as your Industrial Internet of Things (IIoT) environment scales. These challenges include:</p>
<ul>
<li>
<p><strong>Deployment Complexity</strong>: Installing and configuring Node-RED across multiple devices or environments requires technical expertise and attention to detail. For large-scale deployments, managing numerous instances across different devices or servers can become cumbersome and error-prone.</p>
</li>
<li>
<p><strong>Security and Maintenance</strong>: Ensuring your Node-RED instances are secure and up-to-date requires continuous monitoring, timely security patches, and ongoing maintenance. Keeping instances stable and secure can be time-consuming and requires dedicated resources to avoid vulnerabilities.</p>
</li>
<li>
<p><strong>Scalability</strong>: As your IIoT environment grows, scaling your Node-RED infrastructure to handle increased workloads can be challenging. Managing multiple distributed instances often leads to inconsistencies and difficulties in maintaining optimal performance across your entire system.</p>
</li>
<li>
<p><strong>Edge Device Management</strong>: Managing Node-RED instances on edge devices introduces additional complexity. Remote access, secure monitoring, and seamless updates become more difficult as your network expands, especially when dealing with a large number of edge devices.</p>
</li>
</ul>
<p>FlowFuse addresses these challenges by providing a fully managed, secure, and scalable environment for your Node-RED instances. With FlowFuse, you can focus on building and deploying your IIoT solutions, while FlowFuse handles the infrastructure, updates, and scalability. This reduces operational overhead and ensures your instances remain up-to-date and secure.</p>
<p>For more information, read the article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/">Transform Chaos into Control: Centralize Node-RED Management with FlowFuse</a>.</p>
<h2 id="migrating-from-node-red-to-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#migrating-from-node-red-to-flowfuse"></a> Migrating from Node-RED to FlowFuse</h2>
<p>Before you start, make sure you have a FlowFuse Account created. Next, consider how your Node-RED instance needs to be deployed. Decide whether it should run on the edge device or as a cloud instance.</p>
<p>Running Node-RED on an edge device is ideal when your application flow needs direct access to hardware components, such as reading sensors or controlling actuators. This setup allows immediate data processing and control, crucial for applications requiring low latency responses.</p>
<p>On the other hand, if your use case involves monitoring or collecting metrics—such as through MQTT—without needing direct hardware interaction, and you primarily need to transform, contextualize, visualize, or automate repetitive tasks that don't require hardware interaction, a <strong>Cloud Instance</strong> may be more suitable. This option allows you to centralize data collection and processing, making it easier to manage and analyze data from multiple devices.</p>
<p><em>Note: The instructions provided in this article also work for <a href="https://flowfuse.com/docs/install/introduction/">self-hosted</a> FlowFuse environments. Just ensure that when following the steps, you're performing the actions within your self-hosted FlowFuse setup rather than the FlowFuse cloud platform.</em></p>
<h3 id="creating-a-cloud-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#creating-a-cloud-instance"></a> Creating a Cloud Instance</h3>
<p>The FlowFuse snapshot feature, available through the <code>@flowfuse/nr-tools-plugin</code> for self-managed Node-RED (a plugin that allows you to create snapshots from a self-managed Node-RED instance to the FlowFuse platform), However, it does not support direct device snapshots. Instead, you must first create a snapshot for the Cloud Instance and then assign it as the target for your device.</p>
<ol>
<li>Navigate to the FlowFuse platform and log in to your account.</li>
<li>Select the application under which you want to manage your Node-RED instance. You can either choose the default application created with your account or click the "Create Application" button to create a new one.</li>
<li>Once inside the application, select "Add Instance."</li>
<li>Enter a name (or let the system generate one automatically), select the instance type, and choose the Node-RED version that matches your current setup.</li>
<li>Click "Create" to launch the instance.</li>
</ol>
<h3 id="creating-a-device-instance-and-connecting-it-to-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#creating-a-device-instance-and-connecting-it-to-flowfuse"></a> Creating a Device Instance and Connecting It to FlowFuse</h3>
<p>If you need to run Node-RED on the edge device itself but want to manage it remotely, follow the steps below. Skip this step if your Node-RED Application flow can run on the cloud.</p>
<ol>
<li>Navigate to the cloud instance you created above.</li>
<li>Go to the "Devices" tab by clicking on top "Devices" option and select "Add Device".</li>
<li>Once you click "Add Device", a device configuration popup will appear. Copy the command provided and save it for later.</li>
<li>Follow the steps to install the FlowFuse Device Agent on your device as given in this <a href="https://flowfuse.com/docs/device-agent/install/">documentation</a>.</li>
<li>Execute the saved command on the device.</li>
<li>Start the FlowFuse device agent by executing the following command:</li>
</ol>
<div style="position: relative" id="code-container-127">
<pre class="language-bash"><code id="code-127" class="language-bash"> flowfuse-device-agent <span class="token parameter variable">--port</span><span class="token operator">=</span><span class="token number">1881</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-127" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Running on port <code>1881</code> ensures it doesn't conflict with your locally running Node-RED instance, allowing both to run without issues.</p>
<h3 id="creating-essential-backups-before-migration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#creating-essential-backups-before-migration"></a> Creating Essential Backups Before Migration</h3>
<p>Creating a cloud instance is essential for the migration process, regardless of whether your Node-RED instance will run on the device or on the cloud. As discussed earlier, the <code>@flowfuse/nr-tools-plugin</code> does not support creating direct snapshots for devices on the platform. Therefore, you must first create a snapshot for the cloud instance before deploying it to the device.</p>
<p>Follow these steps to create a cloud instance:</p>
<ol>
<li>Install the <code>@flowfuse/nr-tools-plugin</code> into your Node-RED instance via the Palette Manager.</li>
<li>Once installed, open the "FlowFuse tools" tab in the sidebar.</li>
<li>Connect to your FlowFuse Cloud account by clicking the "Connect to FlowFuse" button.<br />
<em>(If you're migrating to a self-hosted FlowFuse instance, ensure you configure the plugin with the correct URL. For detailed steps, refer to the <a href="https://flowfuse.com/docs/migration/node-red-tools/#connecting-to-flowfuse">FlowFuse Node-RED Tools plugin Documentation</a>)</em>.</li>
<li>A browser popup will appear, prompting you to log in to your FlowFuse account. Click "Allow" to grant permission.</li>
<li>After successful authorization, you'll be able to select your team and the associated instance from the "FlowFuse tools" tab.</li>
<li>Choose the team and FlowFuse instance you want to migrate to or the one you will use to take snapshots for your device.</li>
<li>Click the "+ Snapshot" button to create a snapshot. A popup will appear asking for a name and description. Enter the required details and click "Create".</li>
<li>Once the snapshot is created, it will be listed in the sidebar.</li>
<li>To verify, navigate to the FlowFuse platform, go to the FlowFuse Cloud instance you created earlier, and switch to the "Snapshots" tab. The snapshot you created should be visible there.</li>
</ol>
<h4 id="backing-up-system-level-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#backing-up-system-level-environment-variables"></a> Backing Up System-level Environment Variables</h4>
<p>While the snapshot captures flows, credentials, and environment variables at the flow and global level, it does not capture <strong>process environment variables</strong>—those set in the Node-RED <code>settings.json</code> file.</p>
<p>To get these variables, you can use the following flow to dump a list of all process environment variables into the debug window:</p>
<div id="nr-flow-162" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow162 = "\n[{\"id\":\"3ed886625239a5d0\",\"type\":\"function\",\"z\":\"a87879f70edc3463\",\"name\":\"process.env\",\"func\":\"msg.payload = process.env\\nreturn msg;\",\"outputs\":1,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[{\"var\":\"process\",\"module\":\"process\"}],\"x\":650,\"y\":480,\"wires\":[[\"9ca3edbd6857853f\"]]},{\"id\":\"b35ef390a46ff129\",\"type\":\"inject\",\"z\":\"a87879f70edc3463\",\"name\":\"List env vars\",\"props\":[{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":430,\"y\":480,\"wires\":[[\"3ed886625239a5d0\"]]},{\"id\":\"9ca3edbd6857853f\",\"type\":\"debug\",\"z\":\"a87879f70edc3463\",\"name\":\"debug 3\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":880,\"y\":480,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow162.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-162') })</script>
<ol>
<li>Import and deploy the flow into your self-managed Node-RED instance.</li>
<li>Click the Inject Node's button</li>
</ol>
<p>Once clicked, the flow will print all environment variables in the debug window. Identify the environment variables required for your flow and save them in a notepad for later use.</p>
<h4 id="setting-system-level-environment-variables-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#setting-system-level-environment-variables-in-flowfuse"></a> Setting System-Level Environment Variables in FlowFuse</h4>
<p>Now that you have created the snapshots and copied the process environment variables, you need to set these variables in the Node-RED instance to avoid errors during deployment and smooth application running.</p>
<ol>
<li>Go to the your FlowFuse Cloud/Device instance.</li>
<li>Open the Settings tab by clicking the "Settings" option at the top of the page, then select the Environment tab.</li>
<li>Add the environment variables one by one by clicking the "+ Add" button in the bottom-left corner.</li>
<li>After adding all the environment variables, click "Save."</li>
</ol>
<p>For more information refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment Variables in Node-RED</a></p>
<h4 id="migrating-static-assets-to-flowfuse-static-assets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#migrating-static-assets-to-flowfuse-static-assets"></a> Migrating Static Assets to FlowFuse Static Assets</h4>
<p>When working with dashboards or files required in your Node-RED project, these files are typically stored locally and are always available, even if you restart or modify the flows. However, when migrating from Node-RED to a FlowFuse cloud environment, you'll need to manually migrate these files to the cloud-based Node-RED instances. To make this process easier, FlowFuse offers a static assets service feature at the instance level.</p>
<p>Here’s how you can migrate your assets:</p>
<ol>
<li>Locate your static assets in your local system that are used in your Self-managed Node-RED instance from the snapshot we have taken.</li>
<li>Upload them one by one through the Instance "Static Assets" tab. For more information, refer to the <a href="https://flowfuse.com/docs/user/static-asset-service/">Static Asset Service documentation</a>.</li>
</ol>
<p>Once you have migrated all your assets, you will be able to access them in the instance created in the FlowFuse cloud. Just ensure that after deploying the snapshot, the path set in the flow matches the static assets migrated to the FlowFuse instance.</p>
<h3 id="deploying-snapshot-for-the-cloud%2Fdevice-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#deploying-snapshot-for-the-cloud%2Fdevice-instance"></a> Deploying Snapshot for the Cloud/Device instance</h3>
<p>Once the system-level environment variables are set and all static assets have been migrated ( if your Node-RED application flow is using static assets), you can deploy the captured snapshot to either the Cloud or Device instance.</p>
<ol>
<li>
<p>Navigate to the FlowFuse Cloud instance where you created the snapshot by clicking "Hosted Instance" in the left sidebar and selecting the instance from the list.</p>
</li>
<li>
<p>Switch to the "Snapshots" tab at the top to locate the snapshot you created from the list.</p>
</li>
<li>
<p>On the right side of the snapshot, click the three-dot icon:</p>
<ul>
<li>If you are migrating to the Cloud Instance, select <strong>"Restore Snapshot"</strong></li>
<li>If you are migrating to a Device Instance, select <strong>"Set as Device Target"</strong></li>
</ul>
</li>
</ol>
<p>Setting a device target will ensure that the snapshot will be deployed on all of the devices associated with this instance.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/migrating-from-node-red-to-flowfuse/#conclusion"></a> Conclusion</h2>
<p>Migrating from a self-managed Node-RED setup to FlowFuse is simple and offers significant benefits. By transitioning, you’ll reduce operational complexity, enhance security, and improve scalability. FlowFuse handles the infrastructure, so you can focus on building and optimizing your IIoT solutions—whether on edge devices or in the cloud.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Migrating%20from%20Self-Managed%20Node-RED%20to%20FlowFuse-Managed%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/Run FlowFuse Device Agent as a service on MacOS using DockerAutomating FlowFuse Device Agent on macOS with Docker and Colima.2024-11-12T00:00:00ZSumit ShindeRob Marcer<p>The FlowFuse Device Agent is a tool that enables you to run Node-RED on various hardware devices, such as Raspberry Pi, Windows, MacOS, and PLCs. Running Node-RED directly on the device helps when your application flow needs direct access to sensors and actuators connected to the hardware, facilitating seamless integration with the FlowFuse platform. This integration enables secure management, monitoring, and remote editing of flows from a centralized platform, even at the edge.</p>
<!--more-->
<p>In this article, we will explore how to run the FlowFuse Device Agent as a service on MacOS using Docker. This setup ensures that the Device Agent runs in the background, automatically starts on boot, and maintains a continuous connection the FlowFuse platform for remotely managing your Node-RED flows, even after a device restart. This eliminates the need to manually start the agent after each reboot, saving you time and effort.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#prerequisites"></a> Prerequisites</h3>
<p>Before starting, ensure that you have the following set up:</p>
<ul>
<li><strong>FlowFuse Account</strong>: You need an active FlowFuse account to register your device and manage your flows remotely. If you don't have an account, you can <a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Run%20FlowFuse%20Device%20Agent%20as%20a%20service%20on%20MacOS%20using%20Docker">sign up</a> at FlowFuse.</li>
</ul>
<p><em>NOTE: The instructions in this guide were tested on MacBook M1 & M4 MacBook Pro</em></p>
<h3 id="step-1%3A-install-homebrew" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-1%3A-install-homebrew"></a> Step 1: Install Homebrew</h3>
<p>Homebrew is the MacOS package manager for installing packages and libraries. You can install it using the following command:</p>
<div style="position: relative" id="code-container-29">
<pre class="language-bash"><code id="code-29" class="language-bash">/bin/bash <span class="token parameter variable">-c</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="token variable">)</span></span>"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-29" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This script will install the Homebrew package manager on your Mac. Once installed, you can easily install other packages like Docker and Colima.</p>
<h3 id="step-2%3A-install-docker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-2%3A-install-docker"></a> Step 2: Install Docker</h3>
<p>With Homebrew installed, you can now install Docker by running:</p>
<div style="position: relative" id="code-container-39">
<pre class="language-bash"><code id="code-39" class="language-bash">brew <span class="token function">install</span> docker-credential-helper <span class="token function">docker</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-39" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will install Docker and its credential helper, which is useful for managing authentication with Docker registries.</p>
<h3 id="step-3%3A-install-colima" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-3%3A-install-colima"></a> Step 3: Install Colima</h3>
<p>Colima is a free alternative to Docker Desktop, particularly useful for MacOS, and offers better compatibility with Apple Silicon hardware. We’ll need it to run the Flowfuse Device Agent container that we will create later. To install Colima, run:</p>
<div style="position: relative" id="code-container-49">
<pre class="language-bash"><code id="code-49" class="language-bash">brew <span class="token function">install</span> colima</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-49" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="step-4%3A-start-colima" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-4%3A-start-colima"></a> Step 4: Start Colima</h3>
<p>Once Colima is installed, start it with:</p>
<div style="position: relative" id="code-container-56">
<pre class="language-bash"><code id="code-56" class="language-bash">colima start</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-56" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This command starts the Colima virtual machine, which Docker will then use to run containers. If Colima is not running, Docker won't have the necessary environment to create and run containers.</p>
<div style="position: relative" id="code-container-60">
<pre class="language-bash"><code id="code-60" class="language-bash">colima status</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-60" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/colima-status-24-_PIx-cv-1496.avif 1496w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/colima-status-24-_PIx-cv-1496.webp 1496w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="CLI: Showing the result of `colima status`" loading="lazy" decoding="async" src="https://flowfuse.com/img/colima-status-24-_PIx-cv-1496.jpeg" width="1496" height="1022" /></picture>
<em>CLI: Showing the result of <code>colima status</code></em></p>
<h3 id="step-5%3A-set-colima-to-run-as-a-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-5%3A-set-colima-to-run-as-a-service"></a> Step 5: Set Colima to Run as a Service</h3>
<p>To ensure Colima starts automatically in the background, run the following:</p>
<div style="position: relative" id="code-container-70">
<pre class="language-bash"><code id="code-70" class="language-bash">brew services start colima</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-70" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will set Colima to run as a service, so it will start automatically every time your Mac boots up.</p>
<h3 id="step-6%3A-adding-the-device-to-the-flowfuse-platform" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-6%3A-adding-the-device-to-the-flowfuse-platform"></a> Step 6: Adding the Device to the FlowFuse Platform</h3>
<p>Now, you'll need to add a new device to the FlowFuse platform and download the device configuration file. This configuration will allow to connect your MacOS device to your FlowFuse team. For more information on how to add a device and generate the configuration, refer to <a href="https://flowfuse.com/docs/device-agent/register/">Generating "Device Configuration"</a>.</p>
<h3 id="step-7%3A-run-the-flowfuse-device-agent-container" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-7%3A-run-the-flowfuse-device-agent-container"></a> Step 7: Run the FlowFuse Device Agent Container</h3>
<p>You can now run the FlowFuse Device Agent container using Docker. Replace <code>/path/to/device.yml</code> with the actual path to the device configuration file you have downloaded. The following command will launch the container:</p>
<div style="position: relative" id="code-container-86">
<pre class="language-bash"><code id="code-86" class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-d</span> <span class="token parameter variable">--restart</span> unless-stopped <span class="token punctuation">\</span><br /> <span class="token parameter variable">--mount</span> <span class="token assign-left variable">type</span><span class="token operator">=</span>bind,src<span class="token operator">=</span>/path/to/device.yml,target<span class="token operator">=</span>/opt/flowfuse-device/device.yml <span class="token punctuation">\</span><br /> <span class="token parameter variable">-p</span> <span class="token number">1880</span>:1880 flowfuse/device-agent:latest</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-86" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Explanation of the command:</p>
<ul>
<li><code>-d</code>: Run the container in detached mode (in the background).</li>
<li><code>--restart</code> unless-stopped: Ensure the container restarts automatically unless explicitly stopped.</li>
<li><code>--mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml</code>: Mounts your local device.yml file into the container so it can be accessed by the agent.</li>
<li><code>-p 1880:1880</code>: Exposes port 1880 on your host machine, which is typically used for the Node-RED web interface.</li>
<li><code>flowfuse/device-agent:latest</code>: The Docker image for the FlowFuse Device Agent.</li>
</ul>
<h3 id="step-8%3A-verify-the-device-agent-is-running" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-8%3A-verify-the-device-agent-is-running"></a> Step 8: Verify the Device Agent is Running</h3>
<p>To verify that the Device Agent is running correctly, you can use the following command:</p>
<div style="position: relative" id="code-container-123">
<pre class="language-bash"><code id="code-123" class="language-bash"><span class="token function">docker</span> <span class="token function">ps</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-123" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/docker-ps-result-kHveXYNVzQ-1593.avif 1593w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/docker-ps-result-kHveXYNVzQ-1593.webp 1593w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="CLI: Showing the result of `docker ps` indicating device agent is running correctly" loading="lazy" decoding="async" src="https://flowfuse.com/img/docker-ps-result-kHveXYNVzQ-1593.jpeg" width="1593" height="1045" /></picture>
<em>CLI: Showing the result of <code>docker ps</code> indicating the device agent is running correctly</em></p>
<p>This will list all running containers, and you should see the FlowFuse Device Agent listed there. If it's not running, you can check the logs to troubleshoot:</p>
<div style="position: relative" id="code-container-130">
<pre class="language-bash"><code id="code-130" class="language-bash"><span class="token function">docker</span> logs <span class="token operator"><</span>container_id<span class="token operator">></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-130" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Additionally, you can confirm that the Device Agent is running and successfully connected to the FlowFuse platform by following these steps:</p>
<ol>
<li>Navigate to the FlowFuse platform.</li>
<li>In the left sidebar, click on "Edge Devices".</li>
<li>Then, select the device you added for MacOS.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-status-on-ff--XG6uJs2RA-1611.avif 1611w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-status-on-ff--XG6uJs2RA-1611.webp 1611w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="FlowFuse Platform: showing the status of your edge device" loading="lazy" decoding="async" src="https://flowfuse.com/img/device-status-on-ff--XG6uJs2RA-1611.jpeg" width="1611" height="814" /></picture>
<em>FlowFuse Platform: showing the status of your edge device</em></p>
<p>Now, you can start developing applications on the device remotely from any location and manage it efficiently.</p>
<h3 id="step-9%3A-ensure-the-device-agent-restarts-automatically-after-a-reboot" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#step-9%3A-ensure-the-device-agent-restarts-automatically-after-a-reboot"></a> Step 9: Ensure the Device Agent Restarts Automatically After a Reboot</h3>
<p>The <code>--restart unless-stopped</code> flag in the Docker command ensures that your FlowFuse Device Agent container will automatically restart if your Mac reboots. However, it's always good to verify this by restarting your system:</p>
<ol>
<li>Restart your Mac.</li>
<li>After rebooting, check the status of the FlowFuse Device Agent:</li>
</ol>
<div style="position: relative" id="code-container-175">
<pre class="language-bash"><code id="code-175" class="language-bash"> <span class="token function">docker</span> <span class="token function">ps</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-175" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/device-agent-as-service-on-mac/#conclusion"></a> Conclusion</h3>
<p>By following these steps, you've successfully set up the FlowFuse Device Agent on your macOS system using Docker and Colima. Now, the agent will run seamlessly in the background and restart automatically after a system reboot.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/Visual Layout Editor - Now Available in DashboardWith the latest update we have released a new Layout Editor for Dashboard, as well as new widgets and wide-spread improvements.2024-11-08T00:00:00ZJoe Pavitt<p>It has been one of the most requested features for FlowFuse Dashboard, and is now available in its first iteration. It is now possible to resize and move groups in the Dashboard itself using the new "Edit Layout" feature. That's not all though, we've added a new "Spacer" widget to assist with layouts, added improvements to the rendering of charts and plenty more.</p>
<!--more-->
<h2 id="layout-editor---quick-guide" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/#layout-editor---quick-guide"></a> Layout Editor - Quick Guide</h2>
<p>For those transitioning over from the original Node-RED Dashboard 1.0, you'll notice some difference in how you can now edit the layout of your dashboard. Now, the editing is done directly in the Dashboard itself, rather than in the Node-RED editor.</p>
<p>To open the Dashboard in "Edit Mode", click the "Edit Layout" button for the relevant page in the Dashboard sidebar:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-layout-button-XmkdmksdcR-1228.avif 1228w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-layout-button-XmkdmksdcR-1228.webp 1228w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img width="500px" data-zoomable="" alt="Screenshot showing the Edit Layout button in the Dashboard sidebar" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-layout-button-XmkdmksdcR-1228.jpeg" height="284" /></picture>
<em>Screenshot to show the buttons available to "Edit Layout" for a given page</em></p>
<p>This will open the relevant page in "Edit Mode":</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/wysiwyg-demo-arCxQc55qK-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Short recording to show resizing and reordering in the visual layout editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/wysiwyg-demo-arCxQc55qK-800.webp" width="800" height="459" /></picture>
<em>Short recording to show resizing and reordering in the visual layout editor</em></p>
<p>The three controls at the top of the page are:</p>
<ul>
<li><strong>Save Changes:</strong> Deploy any changes to the underlying Node-RED flow.</li>
<li><strong>Discard Changes:</strong> Clear any in-browser changes that have not yet been saved.</li>
<li><strong>Exit Edit Mode:</strong> Stop "Edit Mode" and interact with teh Dashboard as a standard end-user.</li>
</ul>
<p>You can then use the handles on each group to resize them, or click and drag to re-position the groups on the page. How this re-positioning is done is controlled by the "Layout" property of the page. The Visual Editor is currently available for "Grid" and "Fixed" layouts only.</p>
<p>Once you're happy with your changes clicked the "Save Changes" button to deploy them to the underlying Node-RED flow. Note, you will then be made aware that <em>"your flows have been updated"</em> by Dashboard when you return to your Node-RED Editor.</p>
<p>Note that you can only enter "Edit Mode" via the Node-RED Editor, this is to ensure security and stability of your Dashboard, in cases where you may have users for your Dashboard that should not have access to modify your Dashboard's layout.</p>
<h2 id="layout-editor---next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/#layout-editor---next-steps"></a> Layout Editor - Next Steps</h2>
<p>We are very aware that is this is just the first iteration of the Layout Editor, and we have plenty of plans to improve it further. Here are some of the features we are considering for future releases:</p>
<ul>
<li>Include widget resizing and ordering in the Layout Editor</li>
<li>Overhauling sizing options for groups and widgets <a href="https://github.com/FlowFuse/node-red-dashboard/issues/835">#835</a></li>
</ul>
<p>If there are any other key editor features you'd like to see, then please do reach out to us, open GitHub issues and help us shape the future of FlowFuse Dashboard.</p>
<h2 id="new-widget%3A-ui-spacer" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/#new-widget%3A-ui-spacer"></a> New Widget: UI Spacer</h2>
<p>In the Dashboard sidebar, you now have the option to add a "Spacer", this is just an empty widget that can be used to shift the position of other widgets. This can be useful for creating more complex layouts, or for adding space between widgets. For example, if you wanted to have some directional controls with up/down/left/right buttons, you could use a spacer to separate and align them:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/spacer-example-AU_VauCu1g-1688.avif 1688w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/spacer-example-AU_VauCu1g-1688.webp 1688w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="(left) A d-pad controller layout using spacers, (right) the spacer's highlighted to demonstrate their positioning" loading="lazy" decoding="async" src="https://flowfuse.com/img/spacer-example-AU_VauCu1g-1688.jpeg" width="1688" height="522" /></picture>
<em>(left) A d-pad controller layout using spacers, (right) the spacer's highlighted to demonstrate their positioning</em></p>
<p>Spacers can be any width and height, and will always render empty-space. To add a new spacer, you can click the "+" button in the Dashboard sidebar next to any Groups. You can then re-order the widgets there too (re-ordering of widgets in the visual layout editor will be coming soon).</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/#what-else-is-new%3F"></a> What else is new?</h2>
<p>You can find the full 1.19.0 Release Notes <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.19.0">here</a>.</p>
<p>Work has already begun on the next release, <code>1.20.0</code>, you can see what items we have queued up <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">here</a>, if you've got any feedback or suggestions, please do let us know, and feel free to open new issues on our <a href="https://github.com/FlowFuse/node-red-dashboard/issues">GitHub</a></p>
<h2 id="we-are-hiring!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/11/dashboard-new-group-type-app-icon-and-charts/#we-are-hiring!"></a> We are Hiring!</h2>
<p>You may have seen already that we are hiring for a full-time Front-End Engineer to join our team, and work on Dashboard, full-time. If you are interested in working with us, please do check out the job listing <a href="https://job-boards.greenhouse.io/flowfuse/jobs/5185319004">here</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/MQTT Service Now Available on FlowFuseWe 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.2024-10-31T00:00:00ZJoe Pavitt<p>In our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/">recent product update</a> 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 Team and Enterprise teams.</p>
<!--more-->
<p>The MQTT Service is available now on <a href="https://flowfuse.com/product/cloud/">FlowFuse Cloud</a>. FlowFuse permits you to setup your own secure clients to begin publishing and subscribing to your own topics.</p>
<p>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.</p>
<h2 id="use-cases" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/#use-cases"></a> Use Cases</h2>
<p>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:</p>
<ul>
<li>Connect your MQTT enabled PLCs (like Omron N Series, Siemens S7, etc) to your Node-RED instance to open up such possibilities like:
<ul>
<li>Data collection and analysis, Predictive Maintenance, OEE, Condition Based Monitoring.</li>
<li>Triggering actions like sending Emails or alerting your engineers about an event.</li>
<li>Realtime production monitoring of your facility.</li>
</ul>
</li>
<li>Make use of modern IIoT devices on legacy systems by bridging the gap with MQTT.</li>
<li>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.</li>
</ul>
<h2 id="pricing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/#pricing"></a> Pricing</h2>
<p>If you're on the Team or Enterprise tiers of FlowFuse Cloud, then you don't have to pay any extra to get started with the MQTT Service.</p>
<ul>
<li><strong>Team Tier:</strong> Includes <strong>5 clients for free</strong> as part of your existing plan</li>
<li><strong>Enterprise Tier:</strong> Includes <strong>20 clients for free</strong> as part of your existing plan</li>
</ul>
<p>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.</p>
<h2 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/#getting-started"></a> Getting Started</h2>
<p>To get started with your own MQTT Clients, navigate to <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, and Sign In.</p>
<ol>
<li>Click the new "Broker" option in the left navigation menu</li>
<li>Click "Create Client"</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-add-client-L8OdBn73Ts-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-add-client-L8OdBn73Ts-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of FlowFuse's "Create Client" interface" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-add-client-L8OdBn73Ts-1920.jpeg" width="1920" height="1101" /></picture>
<em>Screenshot of FlowFuse's "Create Client" interface</em></p>
<ol start="3">
<li>Fill out the client's credentials (Username + Password)</li>
<li>Define the "Access Control Rules" (see more below)</li>
<li>Click "Confirm"</li>
</ol>
<p>With your client created, you can then, in Node-RED, use the MQTT nodes to connect to your new client.</p>
<p>Setting up a new broker in Node-RED, you can use your credentials accordingly:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-config-RQjXOs9tpu-1294.avif 1294w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-config-RQjXOs9tpu-1294.webp 1294w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img width="400" data-zoomable="" alt="Screenshot of the MQTT Config, "Connection" tab in the Node-RED Editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-config-RQjXOs9tpu-1294.jpeg" height="1024" /></picture>
<em>Screenshot of the MQTT Config, "Connection" tab in the Node-RED Editor</em></p>
<p>And the respective "Security" tab:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-security-BUbOOfKhmr-1302.avif 1302w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-security-BUbOOfKhmr-1302.webp 1302w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img width="440" data-zoomable="" alt="Screenshot of the MQTT Config, "Security" tab in the Node-RED Editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-security-BUbOOfKhmr-1302.jpeg" height="926" /></picture>
<em>Screenshot of the MQTT Config, "Security" tab in the Node-RED Editor</em></p>
<h3 id="access-control-rules" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/#access-control-rules"></a> Access Control Rules</h3>
<p>In MQTT, you can publish and subscribe to <em>topics</em>.</p>
<p>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.</p>
<p>For example, in a flow deployed to many PLCs on your factory floor, you might be publishing to the topics <code>factory/body-shop/plc/1</code>, <code>factory/body-shop/plc/2</code>, etc.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-factory-architecture-Uycrw3e0mg-1452.avif 1452w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-factory-architecture-Uycrw3e0mg-1452.webp 1452w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Simplified example of MQTT data flow for a Factory Floor" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-factory-architecture-Uycrw3e0mg-1452.jpeg" width="1452" height="786" /></picture>
<em>Simplified example of MQTT data flow for a Factory Floor</em></p>
<p>Then, in a Cloud-Hosted flow, you can subscribe to <code>factory/body-shop/plc/#</code> to receive all messages from all PLCs in the body shop, and display relevant data into a Dashboard.</p>
<p>Each client that you create can be constrained in two ways:</p>
<ul>
<li><strong>Action</strong>: You can limit clients to whether than can <em>only</em> subscribe, <em>only</em> publish, or conduct both actions.</li>
<li><strong>Topic</strong>: You can control which topics a given client can interact with.</li>
</ul>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/FlowFuse 2.10: MQTT Broker, Improved Version Control & More!Let's take a look at the new features and improvements in FlowFuse 2.92024-10-24T00:00:00ZJoe Pavitt<p>With FlowFuse 2.10 we've added some new major features, as well as improvements across the board for FlowFuse users.</p>
<p>Most notably, FlowFuse now offers it's own MQTT service, with the option to create MQTT client credentials for your teams, making it even easier and quicker to build your full-stack Node-RED applications.</p>
<!--more-->
<h2 id="mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#mqtt-broker"></a> MQTT Broker</h2>
<p>This is a significant milestone for FlowFuse, as we're now offering our own MQTT service. We have listened to a lot of feedback from users and prospective customers, and this has consistently been a regularly requested offering, and so we are delighted to announce that this is now live on FlowFuse Cloud for our Enterprise teams.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-mqtt-client-config-Pml8r_AhAL-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-mqtt-client-config-Pml8r_AhAL-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the UI to manage your MQTT clients" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-mqtt-client-config-Pml8r_AhAL-1920.jpeg" width="1920" height="1104" /></picture></p>
<p>This feature allows you to run and manage your own MQTT Clients alongside your Node-RED instances, making it easier to build full-stack applications within FlowFuse.</p>
<h3 id="pricing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#pricing"></a> Pricing</h3>
<p>From today, Enterprise teams on FlowFuse Cloud will be able to create up to 20 clients on their accounts at <strong>no extra cost</strong>.</p>
<p>In the near future, users will then be able to purchase additional packs of clients to add to their team.</p>
<p>Self-hosted Enterprise customers will be able to make use of this feature in our next release.</p>
<h2 id="version-history---visual-timeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#version-history---visual-timeline"></a> Version History - Visual Timeline</h2>
<p>Since early iterations of FlowFuse, Snapshots have played a key role in Version Control for your Node-RED flows, environment variables and settings. Whilst we still offer the same "Snapshots" view in the application, we've also added a new view to give a clearer picture of what flows are running on your Node-RED instances and <em>when</em>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-visual-timeline-NDgOJKYJiY-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-visual-timeline-NDgOJKYJiY-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing an Instance's visual timeline" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-visual-timeline-NDgOJKYJiY-1920.jpeg" width="1920" height="1101" /></picture></p>
<p>In this view, you can see every time a new set of flows were deployed to your Node-RED instance, no matter the source, whether that's from the Editor itself, via our <a href="https://flowfuse.com/docs/user/devops-pipelines/">DevOps Pipelines</a>, or restoring a Snapshot.</p>
<h2 id="device-group-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#device-group-environment-variables"></a> Device Group Environment Variables</h2>
<p>We've also added in the ability to define common environment variables in your Device Groups.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-group--with-env-vars-OHb-zt9p3R-1065.avif 1065w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-group--with-env-vars-OHb-zt9p3R-1065.webp 1065w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Device Group Environment Variables" loading="lazy" decoding="async" src="https://flowfuse.com/img/device-group--with-env-vars-OHb-zt9p3R-1065.jpeg" width="1065" height="554" /></picture></p>
<p>These will now be deployed to any devices contained within a given group, and can still be overridden by environment variables defined in the Device's settings directly.</p>
<p>See the <a href="https://flowfuse.com/docs/user/device-groups/">Device Group documentation</a> for more information on how to set environment variables for your device groups.</p>
<h2 id="and-much-more..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#and-much-more..."></a> And Much More...</h2>
<p>For a full list of everything that went into our 2.10 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.10.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.10, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<p>If you have an Enterprise license please make sure to review this <a href="https://flowfuse.com/changelog/2024/08/enterprise-license-update/">changelog entry</a></p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/flowfuse-release-2-10/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/FlowFuse Security Features You Didn’t Know You NeededPowerful Security Features That Enhance Your Node-RED applications2024-10-24T00:00:00ZSumit Shinde<p>When it comes to securing Node-RED applications and its editor, ensuring that your flows and data are protected from unauthorized access can feel like a daunting task. Even after investing considerable time, achieving the right level of security often remains a complex challenge. For enterprises, this goes far beyond access control— security is a cornerstone of protecting sensitive data, maintaining operational continuity, and meeting strict regulatory requirements. A robust security framework not only prevents breaches but also safeguards intellectual property, preserves trust, and shields the organization from costly cyber threats.</p>
<!--more-->
<p>Here are 9 ways FlowFuse simplifies and strengthens your Node-RED deployments, ensuring you’re fully protected without the hassle.</p>
<h2 id="default-security-measures-to-keep-your-environment-safe" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#default-security-measures-to-keep-your-environment-safe"></a> Default Security Measures to Keep Your Environment Safe</h2>
<p>Securing your Node-RED applications is essential to protect sensitive data, proprietary business logic, and critical systems from unauthorized access or cyberattacks. Without proper safeguards, the risks of data loss, operational disruptions, and reputational damage are significant. FlowFuse implements robust security measures right from the start, ensuring your deployments remain safe. Data is encrypted during transmission, and rate limiting prevents traffic overloads, ensuring smooth operations. Additionally, secure tunnelling facilitates safe communication between your edge devices and the FlowFuse platform. To ensure that only authorized personnel have access to your FlowFuse team, we’ve implemented strong login authentication measures, which can be further enhanced with multi-factor authentication as needed.</p>
<p>Here’s the best part- we don’t just offer default protections; we empower you to fine-tune your security defences. FlowFuse features a user-friendly interface that allows you to customize your security settings and design a strategy tailored to your needs. Rest easy knowing that we've laid a solid security foundation while giving you the flexibility to enhance your defences.</p>
<p>If you're interested in learning more about how we safeguard your data privacy and security, we invite you to read our detailed <a href="https://flowfuse.com/product/security/">security statement</a>. Additionally, we are proud to announce that <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/">FlowFuse has achieved SOC 2 Type 1 compliance</a>, demonstrating our commitment to maintaining the highest standards in security and data protection.</p>
<h3 id="single-sign-on-(sso)-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#single-sign-on-(sso)-integration"></a> Single Sign-On (SSO) Integration</h3>
<p>Every organization relies on various tools and platforms to enhance productivity and efficiency. Providing seamless access to these resources is vital for optimizing workflows. That’s where Single Sign-On (SSO) comes in, it simplifies the onboarding and offboarding processes.</p>
<p>With SSO, team members can log in using their existing credentials, eliminating the hassle of remembering multiple passwords. This streamlines their login experience and enables them to be productive from day one.</p>
<p>To implement SSO for your self-hosted FlowFuse, refer to the following resources:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/">How to Set Up SSO SAML for Node-RED</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/">How to Set Up SSO LDAP for Node-RED</a></li>
</ul>
<p>If you are using FlowFuse Cloud, please get in touch with us for configuration assistance.</p>
<h3 id="two-factor-authentication-(2fa)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#two-factor-authentication-(2fa)"></a> Two-Factor Authentication (2FA)</h3>
<p>We've all been there, managing countless passwords, hoping they're strong enough to protect against security threats. But in today’s digital world, passwords alone aren’t sufficient. That’s why Two-Factor Authentication (2FA) has become essential.</p>
<p>FlowFuse understands this need. By enabling 2FA, even if someone gets hold of your password, they'll still require a second form of verification—like a code sent to your phone—to access your account. This simple yet powerful layer of security ensures your data is much safer from unauthorized access. However, when Single Sign-On (SSO) is enabled, 2FA will be replaced by SSO's authentication process.</p>
<p>To set up 2FA in FlowFuse, you’ll need to head over to <strong>User Settings > Security > Two-Factor Authentication</strong>. It’s as simple as clicking the "Enable Two-Factor Authentication" button, scanning the QR code displayed on the platform with your authenticator app, and then entering the code from your app back into FlowFuse. Once you've done that, 2FA will be up and running, adding that extra layer of security to your account!</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/2f-auth-E5uKTU-LH_-1166.avif 1166w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/2f-auth-E5uKTU-LH_-1166.webp 1166w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Two-Factor Authentication" loading="lazy" decoding="async" src="https://flowfuse.com/img/2f-auth-E5uKTU-LH_-1166.jpeg" width="1166" height="746" /></picture>
<em>Flowfuse: Two Factor Authentication</em></p>
<h2 id="granular-role-based-access-management" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#granular-role-based-access-management"></a> Granular Role-Based Access Management</h2>
<p>With collaboration at its core, FlowFuse allows you to create teams and invite members to collaborate on projects. However, not all team members require access to every feature. Effective management is essential, as some members might feel overwhelmed by unnecessary options, and there's a risk of accidental changes being made by those who are unfamiliar with the configurations and settings.</p>
<p>To address this, FlowFuse offers <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">Role-Based Access Control (RBAC)</a>. When inviting team members, you can assign specific roles that provide the appropriate level of access for their work:</p>
<ul>
<li><strong>Owner</strong>: Has full control over the team settings, applications, instances, and flows. Can invite users and change their roles.</li>
<li><strong>Member</strong>: Can access applications and instances and modify flows, but with limited permissions compared to the Owner. Cannot manage team, application, or instance settings or invite users.</li>
<li><strong>Viewer</strong>: Can view instances and flows but cannot make any changes. Ideal for users who need to monitor without editing capabilities.</li>
<li><strong>Dashboard Only</strong>: Restricted to accessing the dashboard or HTTP endpoint. This role is for users who only need to monitor status without making any changes.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/rbac-oWkyoEeToZ-584.avif 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/rbac-oWkyoEeToZ-584.webp 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Role Base Accesss control" loading="lazy" decoding="async" src="https://flowfuse.com/img/rbac-oWkyoEeToZ-584.jpeg" width="584" height="465" /></picture>
<em>Flowfuse: Role Base Accesss control</em></p>
<p>Additionally, you can later change the roles of team members in the "members" page. This helps prevent unauthorized changes and ensures a more secure and efficient workflow.</p>
<h3 id="comprehensive-activity-audit-logs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#comprehensive-activity-audit-logs"></a> Comprehensive Activity Audit Logs</h3>
<p>Today, many organizations prioritize a culture of openness and transparency, but security remains a top concern. Our <strong>Audit Logs</strong> feature supports this dual focus by maintaining a comprehensive record of all actions in the platform. These logs detail who made changes, what was changed, and when it occurred, ensuring accountability and enabling teams to quickly identify any unauthorized access or mistakes that could jeopardize security.</p>
<p>We provide audit logs at three different levels: <strong>instance level</strong>, where all action logs related to a specific instance are recorded; <strong>application level</strong>, which groups logs from instances created within a particular application; and <strong>team level</strong>, where all platform activities are documented but visible only to admins. This layered approach helps organizations maintain secure workflows and demonstrates their commitment to transparency, ensuring that security concerns are effectively addressed without sacrificing openness.</p>
<p>To access the audit logs:</p>
<ul>
<li>For <strong>instance-level logs</strong>, choose the specific instance you want to see and go to <strong>Audit Logs</strong>.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/instance-audit-logs-lQjqLl6RTR-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/instance-audit-logs-lQjqLl6RTR-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Instance-level Audit Logs" loading="lazy" decoding="async" src="https://flowfuse.com/img/instance-audit-logs-lQjqLl6RTR-1919.jpeg" width="1919" height="710" /></picture>
<em>FlowFuse: Instance-level Audit Logs</em></p>
<ul>
<li>For <strong>application-level logs</strong>, select the application you want to view and navigate to <strong>Audit Logs</strong>.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/application-audit-logs-ahzc1LILW1-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/application-audit-logs-ahzc1LILW1-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Application-level Audit Logs" loading="lazy" decoding="async" src="https://flowfuse.com/img/application-audit-logs-ahzc1LILW1-1919.jpeg" width="1919" height="707" /></picture>
<em>Flowfuse: Application-level Audit Logs</em></p>
<ul>
<li>For <strong>team-level logs</strong>, there will be an option labeled <strong>"Audit Logs"</strong> in the left sidebar, accessible only to admins.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/team-audit-logs-YLqPYzVSVY-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/team-audit-logs-YLqPYzVSVY-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Team-level Audit Logs" loading="lazy" decoding="async" src="https://flowfuse.com/img/team-audit-logs-YLqPYzVSVY-1919.jpeg" width="1919" height="723" /></picture>
<em>Flowfuse: Team-level Audit Logs</em></p>
<p>For more information refer to the <a href="https://flowfuse.com/docs/user/logs/#audit-log">Documentation</a></p>
<h3 id="instance-protection-mode" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#instance-protection-mode"></a> Instance Protection Mode</h3>
<p>Imagine your Node-RED application running smoothly on the production line, seamlessly handling critical tasks and data flows. Now, picture the chaos that could ensue if someone from your team accidentally modified a flow. While we offer the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/">snapshot</a> feature to recover previous changes, accidental modifications may not be identified quickly. Even when they are discovered, and the snapshot is used to restore the previous state, it can still take seconds or even minutes to recover, resulting in costly downtime.</p>
<p>To prevent such scenarios, we provide a feature called <strong>Instance Protection Mode</strong>. This mode allows you to set flows within your Node-RED instances to read-only, ensuring that modifications can only occur through a <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/">DevOps pipeline</a>. This process guarantees that even the most critical flows can only be altered with thorough testing and approval.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/instance-protection-camMiCWqNU-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/instance-protection-camMiCWqNU-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Option to Enable the Instance Protection Mode" loading="lazy" decoding="async" src="https://flowfuse.com/img/instance-protection-camMiCWqNU-1919.jpeg" width="1919" height="544" /></picture>
<em>Flowfuse: Option to Enable the Instance Protection Mode</em></p>
<p>With Instance Protection Mode activated, team members can still view flows, but any attempts to modify them are blocked, providing an additional layer of security. This approach protects the integrity of your applications and fosters a controlled environment for making changes safely.</p>
<h3 id="secure-http-nodes-endpoints" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#secure-http-nodes-endpoints"></a> Secure HTTP Nodes Endpoints</h3>
<p>HTTP is one of the most widely used protocols for enabling communication between different applications and services. In Node-RED, you can quickly create these APIs using HTTP-In nodes, which allow for communication. However, while this convenience is excellent, ensuring that only authorized users can access your APIs is essential.</p>
<p>FlowFuse provides robust options for securing all HTTP endpoints served by Flow and the Node-RED Dashboard. To manage this, each instance has a dedicated interface that you can access by navigating to <strong>your instance -> Settings -> Security</strong>. Here, you’ll find several options for securing your APIs:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/http-api-auth-OZiyteRuXD-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/http-api-auth-OZiyteRuXD-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Options to enable authentication for the HTTP endpoints created in the Node-RED instance" loading="lazy" decoding="async" src="https://flowfuse.com/img/http-api-auth-OZiyteRuXD-1919.jpeg" width="1919" height="824" /></picture>
<em>Flowfuse: Options to enable authentication for the HTTP endpoints created in the Node-RED instance.</em></p>
<ol>
<li>
<p><strong>None (Default)</strong>: No authentication is enabled by default, which means anyone can access your endpoints.</p>
</li>
<li>
<p><strong>Basic Authentication</strong>: By selecting this option, two input fields will appear where you can enter a username and password. This ensures that only users with the correct credentials can access your APIs.</p>
</li>
<li>
<p><strong>FlowFuse User Authentication</strong>: This option allows all team members to use their unique usernames and passwords when requesting API.</p>
</li>
<li>
<p><strong>Bearer Tokens</strong>: For more advanced users, there’s an option to generate bearer tokens for secure API access without needing to send usernames and passwords. With this feature, you can also set expiration times for these tokens, ensuring that access is time-limited and reducing the risk of unauthorized use. To use bearer tokens, you must first enable <strong>FlowFuse User Authentication</strong>.</p>
</li>
</ol>
<p>For more information, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/">HTTP Authentication in Node-RED with FlowFuse</a>.</p>
<p>With these features, FlowFuse gives you complete control over who can access your APIs created in the Node-RED instance.</p>
<h3 id="api-token-management-for-secure-platform-interactions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#api-token-management-for-secure-platform-interactions"></a> API Token Management for Secure Platform Interactions</h3>
<p>We understand that organizations need to create integrations for automation, monitoring, and efficient workflows. FlowFuse provides REST APIs that allow easy interaction with various platform parts, including users, instances, teams, devices, and more. However, security settings are protected to ensure they can only be updated by admins or authorized team members directly on the platform, not via APIs.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/genrate-token-for-platform-api-ULarllmKVo-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/genrate-token-for-platform-api-ULarllmKVo-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Options to generate bearer tokens for secure API access" loading="lazy" decoding="async" src="https://flowfuse.com/img/genrate-token-for-platform-api-ULarllmKVo-1919.jpeg" width="1919" height="441" /></picture>
<em>FlowFuse: Interface for generating bearer tokens to ensure secure interactions with the platform APIs.</em></p>
<p>Protecting against unauthorized access is crucial, especially since you control and monitor your entire factory and production lines through this platform. To safeguard this, we offer an interface similar to the one used for bearer tokens. You can access this by navigating to <strong>User Settings > Security > Tokens</strong>.</p>
<p>For more information, refer to the <a href="https://flowfuse.com/docs/api/">FlowFuse Platform API docs</a>.</p>
<h2 id="software-bills-of-materials" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#software-bills-of-materials"></a> Software Bills of Materials</h2>
<p>Node-RED is an open-source platform maintained by dedicated community members who ensure it operates smoothly and remains free of security vulnerabilities. Similarly, there exists a vast ecosystem of open-source packages, nodes, and libraries that we frequently use in our projects. While these packages are often excellent and enhance our capabilities, some may need a regular team or individual to update and monitor them. This can lead to potential risks, as outdated or unmaintained packages can introduce vulnerabilities into our applications.</p>
<p>To address these concerns, we recently introduced the Software Bill of Materials (SBOM) feature, which adds an extra layer of security and compliance. An SBOM is a detailed list of all an application's components. It provides a comprehensive view of all third-party libraries used in each application instance and their latest versions. This allows teams to monitor dependencies and make informed decisions about upgrades, ensuring effective dependency management and enhanced resilience against security threats.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sbom-TLzGNEkGw8-1908.avif 1908w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sbom-TLzGNEkGw8-1908.webp 1908w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Software Bills of Materials" loading="lazy" decoding="async" src="https://flowfuse.com/img/sbom-TLzGNEkGw8-1908.jpeg" width="1908" height="697" /></picture>
<em>FlowFuse: Software Bills of Materials Inteface</em></p>
<p>For more information, refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/">Article on FlowFuse Software Bills of Materials</a>.</p>
<p>In conclusion, FlowFuse offers a comprehensive suite of security features designed to empower you with the tools to protect your Node-RED applications effectively. Understanding and utilizing these security features will help you maintain a secure and efficient environment for your Node-RED applications. With FlowFuse, you can confidently safeguard your deployments, ensuring robust protection against unauthorized access while enhancing collaboration within your team.</p>
<h2 id="get-started-with-flowfuse-today!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-security-features/#get-started-with-flowfuse-today!"></a> Get Started with FlowFuse Today!</h2>
<div>
<p>Ready to secure and scale your Node-RED applications?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=FlowFuse%20Security%20Features%20You%20Didn%E2%80%99t%20Know%20You%20Needed">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/Transform Chaos into Control: Centralize Node-RED Management with FlowFuseWith FlowFuse, you can simplify managing all your Node-RED Instances and remote IoT device management2024-10-18T00:00:00ZSumit Shinde<p>Managing a single Node-RED instance involves setting up and configuring a server, securely tunneling for remote access to edge devices, and ensuring proper networking and firewall configurations, all of which can be complex. The complexity increases when overseeing multiple Node-RED instances spread across various projects, edge devices, or environments.</p>
<!--more-->
<p>This situation brings additional challenges that can make management a really difficult task, often leading to confusion and frustration as teams try to keep everything running smoothly, troubleshoot issues, and ensure clear communication between instances. Consolidating control into a single platform simplifies deployment, configuration, collaboration, and oversight, making it easier to manage multiple Node-RED instances. Let’s explore how FlowFuse can centralize this management.</p>
<h2 id="what-is-a-node-red-instance%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/#what-is-a-node-red-instance%3F"></a> What is a Node-RED Instance?</h2>
<p>A <a href="https://flowfuse.com/node-red/">Node-RED</a> instance refers to a single, operational setup of the Node-RED application. Whether you start Node-RED on your computer, a cloud server, or an edge device, you create an instance. Each instance operates independently, allowing you to build and run automation flows or applications.</p>
<h2 id="what-are-the-challenges-of-managing-multiple-node-red-instances%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/#what-are-the-challenges-of-managing-multiple-node-red-instances%3F"></a> What are the Challenges of Managing Multiple Node-RED Instances?</h2>
<p>Managing multiple Node-RED instances can quickly become complicated as operations grow. Each new instance adds complexity, from configuration issues to security concerns. These challenges highlight the need for a centralized solution to simplify management and improve efficiency.</p>
<ol>
<li>
<p><strong>Deployment and Configuration Management:</strong> Setting up Node-RED instances on a server requires technical knowledge and ongoing maintenance. As the number of instances grows, maintaining them can become time-consuming and resource-intensive.</p>
</li>
<li>
<p><strong>Egde Node-RED Management:</strong> Managing Node-RED instances on edge devices introduces additional challenges, such as the need for on-site troubleshooting when issues arise.</p>
</li>
<li>
<p><strong>Monitoring and Troubleshooting:</strong> Keeping track of the health and performance of multiple instances requires constant attention. Checking logs across different instances can become overwhelming.</p>
</li>
<li>
<p><strong>Security Management:</strong> Each instance requires its own security settings. Ensuring that all instances are secure and up to date can be a difficult task, especially as the number of instances increases.</p>
</li>
<li>
<p><strong>Backup and Recovery:</strong> Having a solid backup and recovery plan is critical. If a system crashes, you need a way to quickly restore it without losing important data.</p>
</li>
<li>
<p><strong>Scaling:</strong> As applications grow in complexity, scaling Node-RED instances becomes necessary. This requires expertise in server management and the ability to handle multiple instances efficiently.</p>
</li>
<li>
<p><strong>Ensuring High Availability:</strong> In production environments, keeping all Node-RED instances running smoothly and avoiding downtime is essential which also requires high technical exepertise</p>
</li>
</ol>
<p>A centralized platform is essential to handle deployment, configuration, and management efficiently, providing a visual interface to maintain and update instances.</p>
<blockquote>
<p>"As organizations navigate the complexities of the digital age, adopting a holistic approach that integrates technology, processes, and people is essential for reaping the full benefits of IoT."</p>
</blockquote>
<h2 id="flowfuse%3A-centralize-your-node-red-and-iot-device-management" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/#flowfuse%3A-centralize-your-node-red-and-iot-device-management"></a> FlowFuse: Centralize Your Node-RED and IoT Device Management</h2>
<p>FlowFuse is a powerful platform designed to simplify the management of multiple Node-RED instances. By providing a centralized interface, FlowFuse enables users to manage, scale, secure, and collaborate on Node-RED solutions.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/instances-m0bUdFxeqD-650.avif 650w, https://flowfuse.com/img/instances-m0bUdFxeqD-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/instances-m0bUdFxeqD-650.webp 650w, https://flowfuse.com/img/instances-m0bUdFxeqD-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/instances-m0bUdFxeqD-650.jpeg 650w, https://flowfuse.com/img/instances-m0bUdFxeqD-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Centralized Node-RED Management" loading="lazy" decoding="async" src="https://flowfuse.com/img/instances-m0bUdFxeqD-650.jpeg" width="1300" height="574" /></picture><br />
<em>Image showing how multiple Node-RED instances are organized and managed under one roof.</em></p>
<p>With FlowFuse, you can organize your Node-RED instances into teams for improved collaboration, allowing seamless teamwork on projects without the need to navigate between different instance locations physically. You can create as many teams as needed, ensuring that instances are organized based on the team members assigned to them. Additionally, you can ensure that each member has the correct permissions they require through role-based access control (RBAC), providing precise management of access and responsibilities.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-650.avif 650w, https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-650.webp 650w, https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-650.jpeg 650w, https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Immersive Editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/imersive-editor-uqTFwx7Twc-650.jpeg" width="1300" height="746" /></picture><br />
<em>Image showing how FlowFuse's immersive editor simplifies managing settings and configuration within the Node-RED editor.</em></p>
<p>FlowFuse also simplifies the <a href="https://flowfuse.com/solutions/edge-connectivity/">monitoring and controlling of edge devices</a> through the <a href="https://flowfuse.com/product/device-agent/">FlowFuse Device Agent</a>, which quickly connects your devices to the cloud platform and allows you to build and monitor applications remotely.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/devices-LjutG883qX-650.avif 650w, https://flowfuse.com/img/devices-LjutG883qX-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/devices-LjutG883qX-650.webp 650w, https://flowfuse.com/img/devices-LjutG883qX-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/devices-LjutG883qX-650.jpeg 650w, https://flowfuse.com/img/devices-LjutG883qX-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Device Management" loading="lazy" decoding="async" src="https://flowfuse.com/img/devices-LjutG883qX-650.jpeg" width="1300" height="565" /></picture><br />
<em>Image showing remote edge devices connected through the FlowFuse platform for remote monitoring and control.</em></p>
<p>Additionally, FlowFuse enables the creation of <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/">DevOps pipelines</a> that ensure your application is well-tested and evaluated before deployment to production. Deploying the same flow to hundreds or thousands of devices becomes effortless with these pipelines.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/devops-lB2nc44WsU-650.avif 650w, https://flowfuse.com/img/devops-lB2nc44WsU-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/devops-lB2nc44WsU-650.webp 650w, https://flowfuse.com/img/devops-lB2nc44WsU-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/devops-lB2nc44WsU-650.jpeg 650w, https://flowfuse.com/img/devops-lB2nc44WsU-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Devops Pipeline" loading="lazy" decoding="async" src="https://flowfuse.com/img/devops-lB2nc44WsU-650.jpeg" width="1300" height="460" /></picture><br />
<em>Image showing feature to create the devops pipeline for Node-RED instances</em></p>
<p>You can efficiently <a href="https://flowfuse.com/docs/user/logs/#logs">monitor logs</a> for each instance and receive instant email alerts if any crashes occur, facilitating quick troubleshooting.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/log-SKkqTlrN9h-650.avif 650w, https://flowfuse.com/img/log-SKkqTlrN9h-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/log-SKkqTlrN9h-650.webp 650w, https://flowfuse.com/img/log-SKkqTlrN9h-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/log-SKkqTlrN9h-650.jpeg 650w, https://flowfuse.com/img/log-SKkqTlrN9h-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Logs" loading="lazy" decoding="async" src="https://flowfuse.com/img/log-SKkqTlrN9h-650.jpeg" width="1300" height="576" /></picture><br />
<em>Image showing the Node-RED instance logs.</em></p>
<p>FlowFuse also allows you to quickly add <a href="https://flowfuse.com/docs/user/high-availability/">high availability</a> features to your instances, ensuring smooth and efficient operation of your production applications. The platform includes an auto-snapshot feature that lets you recover from accidental changes to flows, ensuring you always have a backup of your application.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/high-availablity-NbNmAuvZKN-650.avif 650w, https://flowfuse.com/img/high-availablity-NbNmAuvZKN-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/high-availablity-NbNmAuvZKN-650.webp 650w, https://flowfuse.com/img/high-availablity-NbNmAuvZKN-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/high-availablity-NbNmAuvZKN-650.jpeg 650w, https://flowfuse.com/img/high-availablity-NbNmAuvZKN-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="High availability" loading="lazy" decoding="async" src="https://flowfuse.com/img/high-availablity-NbNmAuvZKN-650.jpeg" width="1300" height="570" /></picture><br />
<em>Image showing the feature that allows to enable high availability for instances</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/snapshots-J2kKsVJcEV-650.avif 650w, https://flowfuse.com/img/snapshots-J2kKsVJcEV-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/snapshots-J2kKsVJcEV-650.webp 650w, https://flowfuse.com/img/snapshots-J2kKsVJcEV-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/snapshots-J2kKsVJcEV-650.jpeg 650w, https://flowfuse.com/img/snapshots-J2kKsVJcEV-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Snapshots" loading="lazy" decoding="async" src="https://flowfuse.com/img/snapshots-J2kKsVJcEV-650.jpeg" width="1300" height="572" /></picture><br />
<em>Image showing snapshots feature</em></p>
<p>We have highlighted just a few features of FlowFuse; there are many more—potentially three to four times what has been presented—and the team is continuously working to develop and introduce new functionalities to improve collaboration, scalability, security, and overall performance.</p>
<h3 id="how-flowfuse-transforms-production-operations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/#how-flowfuse-transforms-production-operations"></a> How FlowFuse Transforms Production Operations</h3>
<p>In manufacturing, downtime is costly, and managing machines, sensors, and systems across multiple sites can be complex. FlowFuse simplifies this by centralizing management, giving you a single platform to oversee all your Node-RED instances efficiently.</p>
<p>With its intuitive interface, FlowFuse handles deployments, updates, and real-time monitoring, ensuring smooth production. It collects data from hardware, APIs, and services using a drag-and-drop interface, enabling teams to easily connect, transform, and analyze data. The high-availability feature ensures critical operations continue even during failures, minimizing downtime.</p>
<p>FlowFuse also enhances security with advanced settings, keeping your systems safe while boosting collaboration. As operations grow, FlowFuse scales seamlessly, integrating new devices and systems without added complexity.</p>
<p>By simplifying system management, FlowFuse cuts costs, keeps production running smoothly, and lets your team focus on growth and innovation.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Transform%20Chaos%20into%20Control%3A%20Centralize%20Node-RED%20Management%20with%20FlowFuse">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/managing-node-red-instances-in-centralize-platfrom/#conclusion"></a> Conclusion</h2>
<p>FlowFuse transforms how you manage Node-RED instances, turning chaos into clarity. With centralized control, teams can collaborate and reduce operational costs while ensuring critical applications remain available and secure. Automated backups and high availability translate to less downtime and more focus on innovation.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/FlowFuse's Software bills of material helps enhance Application Security and ManagementEnhancing the Security and Compliance of Your Solutions2024-10-14T00:00:00ZSumit Shinde<p>FlowFuse recently launched Software Bill of Materials (SBoM) for enterprise customers. This powerful tool enhances security and management within projects, particularly in the Node-RED ecosystem. As open-source libraries and software continue to play a pivotal role in the industry, monitoring third-party components used in projects becomes essential. The SBoM enables organizations to track dependencies and identify vulnerabilities, ensuring compliance and mitigating risks.</p>
<!--more-->
<h2 id="what-is-an-sbom%2C-and-how-does-it-enhance-security%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/#what-is-an-sbom%2C-and-how-does-it-enhance-security%3F"></a> What is an SBoM, and How Does It Enhance Security?</h2>
<p>A Software Bill of Materials (SBoM) is a detailed list of all the components that make up a software application. Just as a bill of materials for a physical product lists every part used in its construction, an SBoM provides a breakdown of all the software libraries, packages, and dependencies in a project.</p>
<p>This transparency is crucial for security, allowing developers and organizations to track what’s inside their software. By knowing precisely what components are in use, you can quickly identify outdated or vulnerable dependencies that may pose security risks. An SBoM helps you monitor third-party nodes, ensuring that any related security issues can be addressed promptly and reducing the chance of vulnerabilities being exploited.</p>
<h2 id="how-you-can-use-sbom-to-enhance-security-in-your-node-red-applications" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/#how-you-can-use-sbom-to-enhance-security-in-your-node-red-applications"></a> How You Can Use SBoM to Enhance Security in Your Node-RED Applications</h2>
<p>Here are some effective strategies for leveraging SBoM in your Node-RED applications:</p>
<ol>
<li>
<p><strong>Regular Monitoring</strong>: Frequently review your SBoM to identify outdated or vulnerable packages. Proactive monitoring helps catch potential security threats early.</p>
</li>
<li>
<p><strong>Timely Upgrades</strong>: When you spot vulnerabilities in your packages, prioritize upgrading them. Keeping dependencies up to date is crucial for maintaining security and performance.</p>
</li>
<li>
<p><strong>Evaluate Third-Party Nodes</strong>: Assess the third-party nodes in your application. If any appear unmaintained or outdated, consider alternatives to ensure ongoing security.</p>
</li>
</ol>
<p>By integrating these practices into your workflow, you can effectively manage dependencies and strengthen the security of your Node-RED solutions.</p>
<h2 id="exploring-the-flowfuse-sbom-feature" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/#exploring-the-flowfuse-sbom-feature"></a> Exploring the FlowFuse SBoM Feature</h2>
<p>To support the effective monitoring and assessment of your dependencies, FlowFuse provides a dedicated Software Bill of Materials (SBoM) interface in the platform. For those who may not be familiar, <strong><a href="https://flowfuse.com/">FlowFuse</a></strong> offers a comprehensive platform that enables engineers to effectively build, manage, and secure their applications. By integrating IT and operational technology (OT) environments, FlowFuse streamlines the process of connecting, collecting, transforming, and visualizing industrial data.</p>
<h3 id="accessing-flowfuse-sbom-interface" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/#accessing-flowfuse-sbom-interface"></a> Accessing FlowFuse SBoM Interface</h3>
<p>The Software Bill of Materials (SBoM) interface is available at the application level. For more information on the application, refer to the <a href="https://flowfuse.com/docs/user/concepts/#application">Documentation</a>. To access it:</p>
<ol>
<li>Navigate to your Node-RED application within the FlowFuse platform.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/applications-options-in-the-ff-c_jGGGYv1t-1900.avif 1900w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/applications-options-in-the-ff-c_jGGGYv1t-1900.webp 1900w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Applications' option in the FlowFuse platform" loading="lazy" decoding="async" src="https://flowfuse.com/img/applications-options-in-the-ff-c_jGGGYv1t-1900.jpeg" width="1900" height="386" /></picture>
<em>Image showing the 'Applications' option in the FlowFuse platform.</em></p>
<ol start="2">
<li>Click the <strong>Dependencies</strong> option at the top to switch to the SBoM interface.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dependencies-tab-option-7KOQMXLw-C-1909.avif 1909w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dependencies-tab-option-7KOQMXLw-C-1909.webp 1909w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Dependencies' option in the FlowFuse platform for the SBoM interface." loading="lazy" decoding="async" src="https://flowfuse.com/img/dependencies-tab-option-7KOQMXLw-C-1909.jpeg" width="1909" height="373" /></picture>
<em>Image showing the 'Dependencies' option in the FlowFuse platform for the SBoM interface.</em></p>
<p><em>Note: This feature is only available for FlowFuse Enterprise customers.</em></p>
<h3 id="understanding-what-the-sbom-interface-shows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-sbom-feature/#understanding-what-the-sbom-interface-shows"></a> Understanding What the SBoM Interface Shows</h3>
<p>Once you navigate the tab, you will see a list of all the packages installed within your Node-RED Cloud instances and devices associated with that application. This includes the package names and versions, the number of devices and instances using each version, and additional details such as the latest available version of each package and the time since its release.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/the-dependency-tab-info-G39QXUOooh-1364.avif 1364w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/the-dependency-tab-info-G39QXUOooh-1364.webp 1364w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the Dependencies tab along with the detailed notes of each item displayed." loading="lazy" decoding="async" src="https://flowfuse.com/img/the-dependency-tab-info-G39QXUOooh-1364.jpeg" width="1364" height="408" /></picture>
<em>Image showing the Dependencies tab along with the detailed notes of each item displayed.</em></p>
<p>Incorporating a Software Bill of Materials into your development process not only enhances security but also fosters a culture of accountability and transparency within your team. By understanding your dependencies, you can make informed decisions that protect your applications and ensure compliance with industry standards.</p>
<div>
<p>Try FlowFuse today with a free trial! Discover how you can enhance your Node-RED projects and accelerate your production processes.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=FlowFuse%27s%20Software%20bills%20of%20material%20helps%20enhance%20Application%20Security%20and%20Management">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/Dialogs, Customizable Icons and Histograms Now Available in FlowFuse DashboardOur latest update for FlowFuse Dashboard introduces a new group type, Dialog, a new chart variation, Histogram and customization support for the application icon.2024-10-11T00:00:00Z<p>This update introduces new ways to enhance data visualization and customization in your dashboards. With key improvements, including a new chart type, customizable app icon support and a dialog feature for groups, this release helps tailor dashboards for better user interaction and flexibility.</p>
<!--more-->
<h2 id="new-chart-type%3A-histogram" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/#new-chart-type%3A-histogram"></a> New Chart Type: Histogram</h2>
<p>Histograms are an essential tool for data analysis, offering a clear way to visualize distributions. The latest Dashboard update introduces a fully customizable histogram chart type, allowing you to present frequency distributions for your data.</p>
<p>Whether analyzing performance metrics or user activity, histograms can give you a clear view of how data points are distributed across predefined ranges. You can now easily group data and control the range, providing deeper insights at a glance.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/chart-histogram-BoJiyvxkPs-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/chart-histogram-BoJiyvxkPs-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Histogram chart type" loading="lazy" decoding="async" src="https://flowfuse.com/img/chart-histogram-BoJiyvxkPs-1920.jpeg" width="1920" height="1067" /></picture>
<em>Screenshot showing the new Histogram chart type</em></p>
<p>The real advantage of this new Histogram chart type is that it simplifies the process for you. Just pass in the raw data, and the histogram will automatically organize it into meaningful ranges, then display how often each range occurs. This makes it incredibly easy to extract valuable insights without the need for manual data processing.</p>
<p>You can see an example flow for the Histogram chart type <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html#histograms">here</a> in our documentation.</p>
<h2 id="customizable-app-icon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/#customizable-app-icon"></a> Customizable App Icon</h2>
<p>Branding is an essential part of any user experience and with this new feature, you can customize your dashboard's app icon. The Node-RED Dashboard 2.0 now allows users to provide their own application icon, which appears in the browser tab and when the dashboard is installed as a Progressive Web App (PWA). This customization helps reinforce your brand, whether you’re developing IoT solutions, monitoring systems or creating dashboards for end-users.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/app-icon-installation-EgW3clBdJZ-938.avif 938w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/app-icon-installation-EgW3clBdJZ-938.webp 938w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img style="max-width: 400px; margin: auto;" data-zoomable="" alt="Screenshot showing the customizable app icon in browser and as a PWA" loading="lazy" decoding="async" src="https://flowfuse.com/img/app-icon-installation-EgW3clBdJZ-938.jpeg" width="938" height="410" /></picture>
<em>Screenshot showing the customizable app icon in browser and as a PWA</em></p>
<p>You can configure the icon by navigating to the base UI settings (ui-base) and providing an icon URL.</p>
<p>You can read more about how to use this feature in the <a href="https://dashboard.flowfuse.com/nodes/config/ui-base.html#application-icon">App Icon Documentation</a></p>
<h2 id="building-dialogs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/#building-dialogs"></a> Building Dialogs</h2>
<p>Organizing data on dashboards has become more efficient with the new "Dialog/Modal" feature. Groups in Dashboard now have a new <a href="https://dashboard.flowfuse.com/nodes/config/ui-group.html#type">"Type"</a> property, so they can be rendered inline as before, or instead rendered as a Dialog. The display of the dialog groups can be controlled (opened/closed) via the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-control.html#show-hide">Control</a> node.</p>
<p>This removes the need for building custom modals and dialogs in a Template node, and makes the entire experience of building your dialogs low-code.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-group-type-dialog-4aF7piua1Z-1842.avif 1842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-group-type-dialog-4aF7piua1Z-1842.webp 1842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing groups rendered as dialogs in the dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-group-type-dialog-4aF7piua1Z-1842.jpeg" width="1842" height="872" /></picture>
<em>Screenshot showing groups rendered as dialogs in the dashboard</em></p>
<p>By utilizing groups as dialogs, users can maintain a clean dashboard while still having quick access to detailed data when required.</p>
<p>You can read more about the new property, and see an example flow in the <a href="https://dashboard.flowfuse.com/nodes/config/ui-group.html#type">UI Group Documentation</a></p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/#what-else-is-new%3F"></a> What else is new?</h2>
<p>You can find the full 1.18.0 Release Notes <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.18.0">here</a>.</p>
<p>Just to highlight a few, particularly valuable, updates and fixes:</p>
<ul>
<li>UI Chart - Group tooltips for line chart.</li>
<li>UI Button Group
<ul>
<li>Show node status</li>
<li>Add pointerdown/pointerup event handling and fix button theming bug</li>
</ul>
</li>
<li>UI Table - Support key type option for entering fixed strings as item labels</li>
<li>UI Switch - Layout Switching with Dynamic Configuration Support</li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/dashboard-new-group-type-app-icon-and-charts/#what's-next%3F"></a> What's Next?</h2>
<p>Work has already begun on the next release, <code>1.19.0</code>, you can see what items we have queued up <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">here</a>, if you've got any feedback or suggestions, please do let us know, and feel free to open new issues on our <a href="https://github.com/FlowFuse/node-red-dashboard/issues">GitHub</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/Exploring Quick Ways to Write Complex Logic in Function Nodes in Node-REDEnhancing Your Node-RED Experience2024-10-09T00:00:00ZSumit Shinde<p>Node-RED is a powerful tool for building automation flows through its visual interface and low-code nodes. However, there are times when this low-code approach falls short, particularly when you need to implement complex JavaScript logic. That’s where the Function Node comes into play. Many Node-RED developers excel in their domains—such as IoT integration and PLCs—but may lack a strong foundation in JavaScript.</p>
<!--more-->
<p>In this guide, I will share strategies for making writing in Function Nodes straightforward and efficient. You’ll learn how to leverage JavaScript's capabilities without needing extensive knowledge, empowering you to handle more complex logic with confidence and ease.</p>
<h2 id="what-are-function-nodes-and-the-challenges-related-to-them%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#what-are-function-nodes-and-the-challenges-related-to-them%3F"></a> What are function nodes and the challenges related to them?</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node_function-X0vNJ12QAC-284.avif 284w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node_function-X0vNJ12QAC-284.webp 284w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/node_function-X0vNJ12QAC-284.jpeg" width="284" height="81" /></picture>
<em>Image showing the function node</em></p>
<h2 id="what-are-function-nodes-and-common-challenges%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#what-are-function-nodes-and-common-challenges%3F"></a> What Are Function Nodes and Common Challenges?</h2>
<p><a href="https://flowfuse.com/node-red/core-nodes/function/">Function Nodes</a> in Node-RED allow you to write custom JavaScript for processing messages. While they provide flexibility, many users find it challenging to turn complex business rules into code and manage variables. For more details on the benefits and drawbacks of using Function Nodes, refer to this <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/">Article</a>.</p>
<p>Before using Function Nodes, consider if existing low-code nodes can fulfill your needs. Using standard low-code nodes can simplify your approach and enhance collaboration and clarity. If you still feel the need to use Function Nodes, don’t worry, In the following section, we’ll explore straightforward strategies to make working with Function Nodes easier.</p>
<h2 id="quick-ways-to-simplify-writing-in-function-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#quick-ways-to-simplify-writing-in-function-nodes"></a> Quick Ways to Simplify Writing in Function Nodes</h2>
<h3 id="using-blockly-based-function-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#using-blockly-based-function-nodes"></a> Using Blockly-Based Function Nodes</h3>
<p>For users seeking to simplify the process of writing complex logic, Blockly-based Function Nodes serve as a valuable tool within Node-RED. Designed to facilitate JavaScript code generation, Blockly allows you to construct logic visually using a drag-and-drop interface with pre-defined blocks. This makes it easier to translate intricate business rules into functional code. As you build your logic, Blockly automatically generates the corresponding JavaScript.</p>
<p>Before proceeding, make sure you have installed the following Node-RED package via the palette manager:</p>
<ul>
<li><strong>node-red-contrib-blockly</strong>: This package adds the Blockly custom node in your Node-RED sidebar to use.</li>
</ul>
<p>For the demonstration, let's consider we have an array of temperatures, and we wanted to calculate the Upper Control Limit (UCL) and Lower Control Limit (LCL) to send to different outputs.</p>
<ol>
<li>Drag the Blockly node onto the canvas and double-click it to open the editor.</li>
<li>Once open, you will see the block categories on the left side, a plain canvas on the right side, and an option to set output and timeout at the bottom. Set the output to 2.</li>
<li>In the left sidebar, you’ll find different categories starting with Node-RED, each containing related operation blocks. For example, in Node-RED, you’ll find blocks to get the value of <code>msg.payload</code>, set <code>msg.payload</code>, and more. In the Math category, there are various blocks for different mathematical operations.</li>
<li>We must first calculate the mean to calculate UCL and LCL. Switch to the Math category and drag the block labeled "<code>sum</code> of the list." Click on the sum in the block to see other options; select "average" from it. Then, in the Node-RED category, find the block labeled "get the <code>msg</code> property from <code>payload</code>" and connect it to the end of the "<code>sum</code> of the list" block. Now, to create a variable to store the mean, switch to the Variables category, click "create variable," and name it <code>mean</code>. Once you make the variable, you’ll get different blocks related to its perform operations on that var, such as setting and changing its value. Drag the block labeled "set to mean" and place it at the start of the "<code>average</code> of list" block.</li>
<li>Next, we know the formulas to calculate UCL and LCL (where we pick z = 3):</li>
</ol>
<ul>
<li>UCL = mean + (stdDev * z)</li>
<li>LCL = mean - (stdDev * z)</li>
</ul>
<ol start="7">
<li>To calculate the standard deviation, switch to the Math category and drag the "<code>sum</code> of the list" block again. Click on the sum and select the "standard deviation" option. Again, drag the block "get the <code>msg</code> property from <code>payload</code>" and connect it to the end of the standard deviation block. Create a variable called <code>stdDev,</code> drag the "set stdDev to" block, and place it at the start of the "<code>standard deviation</code> of the list" block.</li>
<li>Now, it’s time to calculate UCL and LCL. First, create a variable for UCL and then Drag the "set UCL to" block, then switch to the Math category and drag the "1 + 1" block. Place the <code>mean</code> variable in one of the positions for "1." Drag the same block again and place it in the second position of 1, then switch to Variables again and drag the <code>stdDev</code> variable to replace one of the "1s" in the second 1+1 block, and set the second "1" to 3.</li>
<li>Next, switch to Node-RED and drag the "set <code>msg</code> property <code>payload</code> to" block. Then, drag the UCL from the variable and place it in place of "to." s value, Drag the "send "<code>msg</code> block to output 1". Repeat the same steps for LCL, but make sure that you subtract from the mean (<code>stdDev * z</code>) and set LCL to the payload, returning it to output 2.</li>
<li>Finally, click Done to save it.</li>
<li>Drag the inject node, having set the payload to an array of simulated temperature data, and connect its output to the input of the blockly node,</li>
<li>Then, drag the two debug nodes onto the canvas. Connect one debug node to output 1 of the Blockly node (this will display the UCL) and the other debug node to output 2 (this will display the LCL).</li>
<li>Deploy the flow and click the inject button. You will see both UCL and LCL printed on the debug panel</li>
</ol>
<p>The final blockly canvas should look like the below image:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/blockly-eomiksNIVX-951.avif 951w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/blockly-eomiksNIVX-951.webp 951w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing collection of blockly blocks that are calculating UCL and LCL" loading="lazy" decoding="async" src="https://flowfuse.com/img/blockly-eomiksNIVX-951.jpeg" width="951" height="634" /></picture>
<em>Image showing collection of blockly blocks that are calculating UCL and LCL</em></p>
<p>Using Blockly-based Function Nodes simplifies the creation of complex logic in Node-RED. However, a basic understanding of JavaScript is still beneficial, especially as your logic becomes more complicated. While beginners may appreciate the visual interface initially, it can become confusing when trying to implement more advanced features. Additionally, the Blockly Function Node is a modified version of the original Function Node, which may lead to differences in behavior and functionality. Nevertheless, it remains a valuable node for users looking to simplify writing function logics in Node-RED.</p>
<h3 id="using-flowfuse-assistant" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#using-flowfuse-assistant"></a> Using FlowFuse Assistant</h3>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/flowfuse-assistant-1pDkxIiwaO-1912.gif 1912w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the quick function node generation with FlowFuse Assistant" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-assistant-1pDkxIiwaO-1912.webp" width="1912" height="842" /></picture>
<em>Image showing the quick function node generation with FlowFuse Assistant</em></p>
<p>The FlowFuse Assistant is an AI-based plugin integrated into the FlowFuse platform within the Node-RED editor, making it incredibly easy to generate complex functions using prompts.</p>
<p>For this example, let’s use the same logic we demonstrated with Blockly:</p>
<p>Before proceeding, ensure you have updated Node-RED to the latest version on the FlowFuse platform.</p>
<ol>
<li>Open the Node-RED instance editor on the platform.</li>
<li>Click the magic button in the top right corner.</li>
<li>A popup prompt will appear, asking for your input to generate the function node.</li>
<li>Enter the prompt for your logic. For this example, you can use:
<blockquote>
<p>"Generate JavaScript code that takes an array of numbers, calculates the Upper Control Limit (UCL) and Lower Control Limit (LCL), then sends UCL to the first output of the function node and LCL to the second output."</p>
</blockquote>
</li>
<li>Click "Generate." After 2-3 seconds, the Function Node with the requested JavaScript code will appear directly on your canvas.</li>
<li>To test it, connect an inject node containing an array of simulated temperature data, then drag two debug nodes onto the canvas. Connect one debug node to output 1 of the function node (this will display the UCL) and the other debug node to output 2 (this will display the LCL).</li>
<li>Deploy the flow and click the inject button; both UCL and LCL will be displayed on the debug panel.</li>
</ol>
<p>Using the FlowFuse Assistant is significantly easier than Blockly, as it streamlines the process and saves you valuable time. You can articulate your goals in plain English or other languages, such as Spanish or Dutch, and the assistant generates your Function Node seamlessly. This allows you to focus more on your project objectives rather than getting bogged down in coding or block arrangements. Additionally, it provides you with the original Function Node, maintaining standard functionality.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Exploring%20Quick%20Ways%20to%20Write%20Complex%20Logic%20in%20Function%20Nodes%20in%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/quick-ways-to-write-functions-in-node-red/#conclusion"></a> Conclusion</h2>
<p>In summary, both Blockly and FlowFuse Assistant simplify writing complex logic in Node-RED, but FlowFuse is easier to use. Blockly’s visual approach can be confusing, while FlowFuse allows you to generate code by stating your goals in plain English or other languages. Although Blockly can be helpful, it often requires JavaScript knowledge. FlowFuse Assistant simplifies the process, allowing you to focus on your project.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/Using FlowFuse Project Nodes for Faster and More Efficient CommunicationEasy Communication Between Node-RED Instances with FlowFuse2024-10-07T00:00:00ZSumit Shinde<p>Node-RED is a powerful tool for IoT application development, connecting various services and devices. However, establishing communication between different Node-RED instances—whether for monitoring or control—can be complex. This often requires detailed configurations and protocols like MQTT, HTTP, or CoAP, despite its seamless integration with many protocols.</p>
<!--more-->
<p>FlowFuse addresses this challenge with project nodes designed for easy and efficient communication between Node-RED instances. This guide will show you how to use FlowFuse project nodes to enhance communication and integration, complete with practical demonstrations.</p>
<h2 id="what-are-flowfuse-project-nodes%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#what-are-flowfuse-project-nodes%3F"></a> What Are FlowFuse Project Nodes?</h2>
<p><a href="https://flowfuse.com/">FlowFuse</a> is a platform that helps manage multiple Node-RED instances in one place. This centralized management makes it easier for teams to collaborate and share resources while simplifying scaling and enhancing project security.</p>
<p>To facilitate communication between these centrally organized instances, FlowFuse introduces specific project nodes that enable easy and secure message exchange without complex setup. Behind the scenes, these project nodes utilize MQTT, ensuring that communication is lightweight and fast.</p>
<p>These project nodes include three main types:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/project-nodes-IWcroCGTu5-716.avif 716w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/project-nodes-IWcroCGTu5-716.webp 716w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image: Project nodes." loading="lazy" decoding="async" src="https://flowfuse.com/img/project-nodes-IWcroCGTu5-716.jpeg" width="716" height="181" /></picture>
<em>Left :Project-in node, Middle: Project-out node, Right: Project-call node</em></p>
<ul>
<li><strong>Project In</strong>: Listens for messages being broadcast by other Node-RED instances or for messages sent directly to this instance.</li>
<li><strong>Project Out</strong>: Sends messages to other Node-RED instances.</li>
<li><strong>Project Call</strong>:Sends messages to other Node-RED instances to trigger specific flows and waits for a response that can be sent using the project out node.</li>
</ul>
<h2 id="using-flowfuse-project-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#using-flowfuse-project-nodes"></a> Using FlowFuse Project nodes</h2>
<p>In this section, we will explore how to use the FlowFuse project nodes. We’ll begin by explaining the Project In and Project Out nodes, followed by a detailed look at the Project Call node.</p>
<h3 id="using-project-in-and-out-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#using-project-in-and-out-nodes"></a> Using Project in and out nodes</h3>
<p>To demostrate this nodes, we will use an example where a central instance monitors the CPU performance of multiple Node-RED instances every 10 seconds.</p>
<p>Before we start, let’s understand their configurations:</p>
<h4 id="project-in-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#project-in-node"></a> Project In Node</h4>
<ul>
<li><strong>Name</strong>: A descriptive label for the node.</li>
<li><strong>Source</strong>:
<ul>
<li><strong>Receive</strong>: Select this option to receive messages sent specifically to this instance.</li>
<li><strong>Receive Broadcast From</strong>: Choose this option to listen for messages broadcast from other instances. A dropdown will include the names of all instances within your team.</li>
</ul>
</li>
<li><strong>Topic</strong>: This field allows you to specify a topic, similar to MQTT, to categorize the messages.</li>
</ul>
<h4 id="project-out-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#project-out-node"></a> Project Out Node</h4>
<ul>
<li><strong>Name</strong>: A descriptive label for the node.</li>
<li><strong>Mode</strong>:
<ul>
<li><strong>Send Specified Project Node</strong>: This option allows you to send a specific message or payload to the selected instance. Use this for one-way communication.</li>
<li><strong>Return Project Link Call</strong>: This option sends used to sent response back using a Project Out node project call node.</li>
</ul>
</li>
<li><strong>Target</strong>:
<ul>
<li><strong>Send Message To Instance</strong>: A dropdown to select the instance.</li>
<li><strong>Broadcast Messages</strong>: Option to send messages to all instances.</li>
</ul>
</li>
<li><strong>Topic</strong>: This field allows you to specify a topic, similar to MQTT, to categorize the messages.</li>
</ul>
<h2 id="example%3A-monitoring-cpu-performance-of-node-red-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#example%3A-monitoring-cpu-performance-of-node-red-instances"></a> Example: Monitoring CPU Performance Of Node-RED Instances</h2>
<h2 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#prerequisites"></a> Prerequisites</h2>
<p>Before we begin, ensure you have installed the following nodes via the Palette Manager:</p>
<ul>
<li><strong>node-red-contrib-cpu</strong>: This node is essential for monitoring CPU performance. Install it on the instances that you want to monitor.</li>
<li><strong>@flowfuse/node-red-dashboard</strong>: This node is used for creating a dashboard interface. Install it on the central instance, as we will be visualizing the CPU performance data collected from the monitored instances.</li>
</ul>
<h3 id="creating-the-flow-on-the-instances-to-monitor" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#creating-the-flow-on-the-instances-to-monitor"></a> Creating the Flow on the Instances to Monitor</h3>
<p>In this section, we will learn how to create a flow that monitors CPU performance and sends data using <strong>Project out</strong>. We will also discuss how to make this flow reusable, allowing you to apply the same setup across multiple instances without editing the flow each time.</p>
<ol>
<li>Drag the <strong>Inject</strong> node onto the canvas and set the repeat interval to "2 seconds". This will trigger the flow after every 2 seconds.</li>
<li>Drag the <strong>CPU</strong> node onto the canvas. Double-click it to configure, enable the option "Send message for overall usage," and click "Done".</li>
<li>Drag the <strong>Change</strong> node onto the canvas. Configure it to add metadata, including the instance name, instance ID, using default environment variablesand and the CPU data received from the CPU node.</li>
<li>Drag the <strong>Project Out</strong> node onto the canvas. Double-click it to set the Mode to "Send to specified project node." For the Target, select "Send message to instance" and choose the name of your centralized instance.</li>
<li>Next, Enter the topic as "cpu-performance".</li>
<li>Connect the output of the <strong>Inject</strong> node to the input of the <strong>CPU</strong> node, the output of the <strong>CPU</strong> node to the input of the <strong>Change</strong> node, and finally, connect the <strong>Change</strong> node to the <strong>Project Out</strong> node.</li>
</ol>
<div id="nr-flow-155" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow155 = "\n[{\"id\":\"dd182fedce6c532a\",\"type\":\"inject\",\"z\":\"fe61f54c8563279d\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":190,\"y\":200,\"wires\":[[\"c47bc42f8a6f6884\"]]},{\"id\":\"e0dd915a7b37af2b\",\"type\":\"project link out\",\"z\":\"fe61f54c8563279d\",\"name\":\"project out 1\",\"mode\":\"link\",\"broadcast\":false,\"project\":\"054bb5cf-20df-431f-a00b-29b28e160b27\",\"topic\":\"cpu-performance\",\"x\":770,\"y\":200,\"wires\":[]},{\"id\":\"05a9ec7b2ae57cc0\",\"type\":\"change\",\"z\":\"fe61f54c8563279d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"data.name\",\"pt\":\"msg\",\"to\":\"FF_INSTANCE_NAME\",\"tot\":\"env\"},{\"t\":\"set\",\"p\":\"data.cpu\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":560,\"y\":200,\"wires\":[[\"e0dd915a7b37af2b\"]]},{\"id\":\"c47bc42f8a6f6884\",\"type\":\"cpu\",\"z\":\"fe61f54c8563279d\",\"name\":\"\",\"msgCore\":false,\"msgOverall\":true,\"msgArray\":false,\"msgTemp\":false,\"x\":370,\"y\":200,\"wires\":[[\"05a9ec7b2ae57cc0\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow155.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-155') })</script>
<p>By utilizing environment variables, this flow becomes reusable, allowing you to copy and paste flow to monitor multple instances.</p>
<h3 id="receiving-data-to-monitor-and-visualize" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#receiving-data-to-monitor-and-visualize"></a> Receiving Data to Monitor and Visualize</h3>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/device-monitoring-chart-9xsD0nWkND-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image: Line chart visualizing CPU performance of all instances." loading="lazy" decoding="async" src="https://flowfuse.com/img/device-monitoring-chart-9xsD0nWkND-800.webp" width="800" height="263" /></picture>
<em>Image: Line chart visualizing CPU performance of all instances.</em></p>
<p>To receive the CPU data from monitored instances, follow these steps in your centralized Node-RED instance:</p>
<ol>
<li>Open the editor for your central instance.</li>
<li>Drag the <strong>Project In</strong> node onto the canvas.</li>
<li>Double-click the <strong>Project In</strong> node and set the <strong>Source</strong> to "Listen for broadcast messages from." Select the name of the instances you want to monitor from the dropdown. If you want data from all instances, select "All instances and devices."</li>
<li>Enter the <strong>Topic</strong> as "cpu-performance" (or the topic you configured in the Project Out nodes).</li>
<li>Drag a <strong>ui-chart</strong> widget onto the canvas. Set the chart type to "line" and configure the series to <code>msg.payload</code>.</li>
<li>Connect the output of the <strong>Project In</strong> node to the input of the <strong>ui-chart</strong> node.</li>
<li>Click the "deploy" button.</li>
</ol>
<div id="nr-flow-156" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow156 = "\n[{\"id\":\"2d681b19eb5799b7\",\"type\":\"change\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data.cpu\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"data.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":400,\"y\":220,\"wires\":[[\"307d9c5913509557\"]]},{\"id\":\"307d9c5913509557\",\"type\":\"ui-chart\",\"z\":\"d382bd2b5733a3a9\",\"group\":\"d0dbd4016c7aac21\",\"name\":\"\",\"label\":\"chart\",\"order\":1,\"chartType\":\"line\",\"category\":\"topic\",\"categoryType\":\"msg\",\"xAxisLabel\":\"\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"property\",\"xAxisType\":\"time\",\"xAxisFormat\":\"\",\"xAxisFormatType\":\"auto\",\"yAxisLabel\":\"\",\"yAxisProperty\":\"\",\"ymin\":\"\",\"ymax\":\"\",\"action\":\"append\",\"stackSeries\":false,\"pointShape\":\"cross\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":[\"#0095ff\",\"#ff0000\",\"#ff7f0e\",\"#2ca02c\",\"#a347e1\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"textColor\":[\"#666666\"],\"textColorDefault\":true,\"gridColor\":[\"#e5e5e5\"],\"gridColorDefault\":true,\"width\":\"12\",\"height\":8,\"className\":\"\",\"x\":630,\"y\":220,\"wires\":[[]]},{\"id\":\"465ee550bf9d101d\",\"type\":\"project link in\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"project in 1\",\"project\":\"all\",\"broadcast\":false,\"topic\":\"cpu-performance\",\"x\":180,\"y\":220,\"wires\":[[\"2d681b19eb5799b7\"]]},{\"id\":\"d0dbd4016c7aac21\",\"type\":\"ui-group\",\"name\":\"Device Monitoring Chart\",\"page\":\"39fae809f6f7fc7b\",\"width\":\"12\",\"height\":\"1\",\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"39fae809f6f7fc7b\",\"type\":\"ui-page\",\"name\":\"Device Monitoring\",\"ui\":\"ded86f3820342985\",\"path\":\"/charts-example\",\"icon\":\"chart-box-outline\",\"layout\":\"grid\",\"theme\":\"5075a7d8e4947586\",\"breakpoints\":[{\"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\":\"ded86f3820342985\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"showPathInSidebar\":false,\"showPageTitle\":true,\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\"},{\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow156.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-156') })</script>
<p>Once deployed, open the dashboard to view an interactive live line chart displaying the CPU performance of all monitored devices.</p>
<h4 id="visualizing-data-for-specific-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#visualizing-data-for-specific-devices"></a> Visualizing Data for Specific Devices</h4>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/device-monitoring-gauges-xl9JBdLTFd-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image: Gauges visualizing the CPU performance of different devices." loading="lazy" decoding="async" src="https://flowfuse.com/img/device-monitoring-gauges-xl9JBdLTFd-800.webp" width="800" height="136" /></picture>
<em>Image: Gauges visualizing the CPU performance of different devices.</em></p>
<p>To visualize CPU data for specific devices separately, configure the <strong>Project In</strong> node to "Listen for broadcast messages from" and select the desired instance name that you want to monitor.</p>
<p>In the instance sending the CPU data for monitoring, set the <strong>Project Out</strong> node to "Broadcast messages." This will send CPU data to all instances within your team, allowing the centralized instance to capture and display the information.</p>
<p>If you prefer not to use broadcasting, configure the <strong>Project Out</strong> node with specific topics. This will ensure that receiving nodes capture only specific instance data based on the topic. You can then connect these nodes to gauge nodes to distinctly display the CPU performance for each instance.</p>
<div id="nr-flow-157" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow157 = "\n[{\"id\":\"c0021ca894dd3e17\",\"type\":\"change\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data.cpu\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"ui_update.label\",\"pt\":\"msg\",\"to\":\"data.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":360,\"y\":140,\"wires\":[[\"932a6733d53ab87d\"]]},{\"id\":\"426925e74d8e30f5\",\"type\":\"project link in\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"project in 1\",\"project\":\"04175120-ebeb-4813-8910-03f92f8ed429\",\"broadcast\":true,\"topic\":\"cpu-performance\",\"x\":140,\"y\":140,\"wires\":[[\"c0021ca894dd3e17\"]]},{\"id\":\"932a6733d53ab87d\",\"type\":\"ui-gauge\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"group\":\"087559f9b99f047a\",\"order\":1,\"width\":\"4\",\"height\":\"4\",\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"gauge\",\"units\":\"units\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":[{\"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\":590,\"y\":140,\"wires\":[]},{\"id\":\"38539dc4bfdbf6be\",\"type\":\"change\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data.cpu\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"ui_update.label\",\"pt\":\"msg\",\"to\":\"data.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":360,\"y\":240,\"wires\":[[\"bdbcd5f9f8ac610c\"]]},{\"id\":\"e36b12aa51d365fe\",\"type\":\"project link in\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"project in 2\",\"project\":\"04175120-ebeb-4813-8910-03f92f8ed429\",\"broadcast\":true,\"topic\":\"cpu-performance\",\"x\":140,\"y\":240,\"wires\":[[\"38539dc4bfdbf6be\"]]},{\"id\":\"bdbcd5f9f8ac610c\",\"type\":\"ui-gauge\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"group\":\"6cf5326fe928c9cf\",\"order\":1,\"width\":\"4\",\"height\":\"4\",\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"gauge\",\"units\":\"units\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":[{\"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\":590,\"y\":240,\"wires\":[]},{\"id\":\"a2e1fba0e9cd985a\",\"type\":\"change\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"data.cpu\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"ui_update.label\",\"pt\":\"msg\",\"to\":\"data.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":360,\"y\":340,\"wires\":[[\"c298b64b260df708\"]]},{\"id\":\"dcfe73c16e17648a\",\"type\":\"project link in\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"project in 3\",\"project\":\"8a611136-6e3f-447e-9436-34b2d00eac8e\",\"broadcast\":true,\"topic\":\"cpu-performance\",\"x\":140,\"y\":340,\"wires\":[[\"a2e1fba0e9cd985a\"]]},{\"id\":\"c298b64b260df708\",\"type\":\"ui-gauge\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"group\":\"ee5fc3eb29a7ee4b\",\"order\":1,\"width\":\"4\",\"height\":\"4\",\"gtype\":\"gauge-half\",\"gstyle\":\"needle\",\"title\":\"gauge\",\"units\":\"units\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":[{\"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\":590,\"y\":340,\"wires\":[]},{\"id\":\"087559f9b99f047a\",\"type\":\"ui-group\",\"name\":\"Device Group 1\",\"page\":\"39fae809f6f7fc7b\",\"width\":\"4\",\"height\":\"2\",\"order\":3,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"6cf5326fe928c9cf\",\"type\":\"ui-group\",\"name\":\"Device Group 2\",\"page\":\"39fae809f6f7fc7b\",\"width\":\"4\",\"height\":\"2\",\"order\":2,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"ee5fc3eb29a7ee4b\",\"type\":\"ui-group\",\"name\":\"Device Group 3\",\"page\":\"39fae809f6f7fc7b\",\"width\":\"4\",\"height\":\"2\",\"order\":1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"39fae809f6f7fc7b\",\"type\":\"ui-page\",\"name\":\"Device Monitoring\",\"ui\":\"ded86f3820342985\",\"path\":\"/charts-example\",\"icon\":\"chart-box-outline\",\"layout\":\"grid\",\"theme\":\"5075a7d8e4947586\",\"breakpoints\":[{\"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\":\"ded86f3820342985\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"showPathInSidebar\":false,\"showPageTitle\":true,\"navigationStyle\":\"default\",\"titleBarStyle\":\"default\"},{\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow157.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-157') })</script>
<p>This example showcases one of many powerful use cases for FlowFuse project nodes. By utilizing these capabilities, you can transform how your Node-RED instances communicate, enabling efficient workflows and innovative solutions.</p>
<h2 id="using-project-call-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#using-project-call-nodes"></a> Using Project Call Nodes</h2>
<p><strong>Project Call</strong> nodes are ideal for triggering flows deployed on another Node-RED instance and retrieving the final result as a response. While they function similarly to <a href="https://flowfuse.com/node-red/integration-technologies/webhook/">webhooks</a>, they utilize <a href="https://flowfuse.com/node-red/protocol/mqtt/">MQTT</a> as their underlying mechanism instead of HTTP. In this section, we will demonstrate the use of a <strong>Project Call</strong> node through an example of making an on-demand temperature request.</p>
<h3 id="how-project-call-nodes-work" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#how-project-call-nodes-work"></a> How Project Call Nodes Work</h3>
<p>The <strong>Project Call</strong> node does not operate in isolation; it requires both <strong>Project In</strong> and <strong>Project Out</strong> nodes to function properly. The <strong>Project Call</strong> node triggers the <strong>Project In</strong> node deployed on the specified target instance, while the <strong>Project Out</strong> node handles the response. This means the flow that needs to be triggered should start with a <strong>Project In</strong> node and end with a <strong>Project Out</strong> node.</p>
<h3 id="example%3A-on-demand-temperature-request" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#example%3A-on-demand-temperature-request"></a> Example: On-Demand Temperature Request</h3>
<p>In this example, we will trigger a flow on a Raspberry Pi instance that reads the temperature using a DHT11 sensor and sends the response back to the instance where we use the <strong>Project Call</strong> node.</p>
<p>Before we start, let’s review the configuration options for the <strong>Project Call</strong> node:</p>
<h4 id="project-call-node-configuration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#project-call-node-configuration"></a> Project Call Node Configuration</h4>
<ul>
<li><strong>Name</strong>: A descriptive label for the node.</li>
<li><strong>Timeout</strong>: Set the duration to wait for a response before timing out.</li>
<li><strong>Target</strong>: Specify the target instance where the flow is deployed.</li>
<li><strong>Topic</strong>: This field allows you to specify a topic, similar to MQTT, to categorize the messages.</li>
</ul>
<h4 id="prerequisites-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#prerequisites-1"></a> Prerequisites</h4>
<p>Before we begin, ensure you have the following prepared:</p>
<ul>
<li><strong>node-red-contrib-dht-sensor</strong>: Install this node via the palette manager. This node is used to manage the connection to a DHT11 or DHT22 sensor on a Raspberry Pi.</li>
<li><strong>FlowFuse Device Agent Setup</strong>: Ensure you have set up and are running the FlowFuse device agent on your Raspberry Pi, and it is connected to the platform.</li>
</ul>
<h4 id="developing-a-flow-to-handle-on-demand-data-requests" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#developing-a-flow-to-handle-on-demand-data-requests"></a> Developing a Flow to Handle On-Demand Data Requests</h4>
<p>Throughout this section, we will explore how to utilize the <strong>Project Call</strong> node along with <strong>Project In</strong> and <strong>Project Out</strong> nodes to trigger a flow that retrieves temperature readings from a Raspberry Pi on demand.</p>
<p><em><strong>Note</strong>: Before proceeding, make sure your device is assigned to an instance. If the device is assigned to an application, you cannot use Project nodes, as they are designed to work with instances.</em></p>
<div id="nr-flow-158" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow158 = "\n[{\"id\":\"bdb27b9ab0d94897\",\"type\":\"inject\",\"z\":\"b152a914653d9fce\",\"name\":\"Trigger\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"5\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":260,\"y\":380,\"wires\":[[\"8a4fe9cf342b6156\"]]},{\"id\":\"4ece54ca3c91f7ff\",\"type\":\"debug\",\"z\":\"b152a914653d9fce\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":680,\"y\":380,\"wires\":[]},{\"id\":\"8a4fe9cf342b6156\",\"type\":\"rpi-dht22\",\"z\":\"b152a914653d9fce\",\"name\":\"\",\"topic\":\"rpi-dht22\",\"dht\":\"11\",\"pintype\":\"0\",\"pin\":4,\"x\":460,\"y\":380,\"wires\":[[\"4ece54ca3c91f7ff\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow158.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-158') })</script>
<ol>
<li>Copy/download the flow above and import/upload it into your Raspberry Pi Node-RED instance. Ensure you have correctly interfaced the DHT11 sensor with your Raspberry Pi. For more information, refer to our guide on <a href="https://flowfuse.com/node-red/hardware/raspberry-pi-4/">Setting Up Node-RED on Raspberry Pi 4</a>, which explains how to install the device agent on the Raspberry Pi and read temperature data from the DHT11 sensor.</li>
<li>Deploy the flow.</li>
</ol>
<p>Once you deploy the flow, you will see the temperature data displayed on the Debug panel if everything was done correctly. Now, let’s add the project nodes to trigger the temperature reading flow on demand and retrieve the data accordingly.</p>
<ol start="3">
<li>Drag the <strong>Project In</strong> node onto the canvas, double-click on it, and set the source to "Receive messages sent to this instance." Next, enter the topic.</li>
<li>Replace the <strong>Inject</strong> node with the <strong>Project In</strong> node.</li>
<li>Next, you can add a <strong>Change</strong> node to format the data received by the <strong>DHT</strong> node.</li>
<li>Drag the <strong>Project Out</strong> node onto the canvas, double-click on it, and set the mode to "Return to project link call."</li>
</ol>
<p>Once the flow is ready, deploy it. The final flow should look like the one below:</p>
<div id="nr-flow-159" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow159 = "\n[{\"id\":\"020ede0bf68cf063\",\"type\":\"change\",\"z\":\"fe61f54c8563279d\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"data.temperature\",\"pt\":\"msg\",\"to\":\"payload\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"data.humidity\",\"pt\":\"msg\",\"to\":\"humidity\",\"tot\":\"msg\"},{\"t\":\"delete\",\"p\":\"payload\",\"pt\":\"msg\"},{\"t\":\"delete\",\"p\":\"humidity\",\"pt\":\"msg\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"rpi-dht11\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":560,\"y\":220,\"wires\":[[\"1a592414ab69f1d8\"]]},{\"id\":\"a39fcfcd133ecaae\",\"type\":\"project link in\",\"z\":\"fe61f54c8563279d\",\"name\":\"project in 1\",\"project\":\"all\",\"broadcast\":false,\"topic\":\"temperature\",\"x\":160,\"y\":220,\"wires\":[[\"16e8a24545d3258f\"]]},{\"id\":\"1a592414ab69f1d8\",\"type\":\"project link out\",\"z\":\"fe61f54c8563279d\",\"name\":\"project out 1\",\"mode\":\"return\",\"broadcast\":false,\"project\":\"b1dd1d7d-556e-4dd4-9b8f-d78ffe3f510d\",\"topic\":\"\",\"x\":790,\"y\":220,\"wires\":[]},{\"id\":\"16e8a24545d3258f\",\"type\":\"rpi-dht22\",\"z\":\"fe61f54c8563279d\",\"name\":\"\",\"topic\":\"rpi-dht11\",\"dht\":\"11\",\"pintype\":\"0\",\"pin\":4,\"x\":340,\"y\":220,\"wires\":[[\"020ede0bf68cf063\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow159.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-159') })</script>
<p>Now that we have added the <strong>Project In</strong> and <strong>Project Out</strong> nodes, the flow can be triggered to read the temperature from any Node-RED instance within your team using the <strong>Project Call</strong> node and receive the response.</p>
<h4 id="triggering-the-flow-and-receiving-temperature-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#triggering-the-flow-and-receiving-temperature-data"></a> Triggering the Flow and Receiving Temperature Data</h4>
<p>In this section, we will explore how to trigger the flow we created in the previous step and receive the on-demand temperature data.</p>
<ol>
<li>Navigate to the instance within your team where you want to receive the data.</li>
<li>Drag the <strong>Project Call</strong> node onto the canvas. Double-click it to set the timeout according to your preference, then set the target to the instance where your flow needs to be triggered. Next, enter the topic you configured in the <strong>Project In</strong> node previously.</li>
<li>Drag an <strong>Inject</strong> node onto the canvas and connect it to the input of the <strong>Project Call</strong> node. This will allow you to trigger the <strong>Project Call</strong> node manually.</li>
<li>Finally, drag a <strong>Debug</strong> node onto the canvas and connect it to the output of the <strong>Project Call</strong> node. This will help you view the response received from the triggered flow in the Debug panel.</li>
<li>Deploy the flow.</li>
</ol>
<div id="nr-flow-160" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow160 = "\n[{\"id\":\"8276fba516f3697b\",\"type\":\"project link call\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"project\":\"d1b6cf3a-70fc-4ec4-b30c-e207338b2cc4\",\"topic\":\"temperature\",\"timeout\":\"30\",\"x\":530,\"y\":200,\"wires\":[[\"9ef6638382d7e9d5\"]]},{\"id\":\"0abdbf43350eb362\",\"type\":\"inject\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":320,\"y\":200,\"wires\":[[\"8276fba516f3697b\"]]},{\"id\":\"9ef6638382d7e9d5\",\"type\":\"debug\",\"z\":\"d382bd2b5733a3a9\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"true\",\"targetType\":\"full\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":740,\"y\":200,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow160.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-160') })</script>
<p>Now, once you click the <strong>Inject</strong> button, you will see the response that includes the temperature in the debug panel, which is read by the flow deployed on the Raspberry Pi.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/project-out-node-triggering-flow-_HySgjvBEB-1908.gif 1908w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the project call node triggering the flow to read the temperature data" loading="lazy" decoding="async" src="https://flowfuse.com/img/project-out-node-triggering-flow-_HySgjvBEB-1908.webp" width="1908" height="832" /></picture>
<em>Image showing the project call node triggering the flow deployed on the device to read the temperature data.</em></p>
<p>Now that you understand how to use FlowFuse project nodes, you can significantly improve the way your Node-RED instances communicate with one another.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/exploring-flowfuse-project-nodes/#conclusion"></a> Conclusion</h2>
<p>FlowFuse project nodes streamline communication between Node-RED instances. By using these nodes, you can easily monitor performance, request data, and more. This makes your workflows smoother and more efficient.</p>
<div>
<p>Streamline communication between your Node-RED instances—manage, scale, and secure them with ease to enhance industrial efficiency.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Using%20FlowFuse%20Project%20Nodes%20for%20Faster%20and%20More%20Efficient%20Communication">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/Creating and Automating DevOps Pipelines for Node-RED in Industrial EnvironmentsStreamlining Deployments for Efficiency and Safety in Industrial Environments2024-10-03T00:00:00ZSumit ShindeSteve McLaughlin<p>When deploying any update in your manufacturing or automotive process, it is important to perform testing and validation. The smallest errors can lead to a production outage. In such cases, having a quick and reliable deployment process is essential for maximising uptime and minimising downtime.</p>
<!--more-->
<p>For developers using Node-RED, setting up a comprehensive DevOps pipeline can make all the difference. In this blog, we’ll explore how to build and automate DevOps pipelines specifically for Node-RED deployments. You’ll discover practical tips and tools to streamline your process, ensuring your applications are always ready to support your operations.</p>
<h2 id="what-exactly-is-a-devops-pipeline%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/#what-exactly-is-a-devops-pipeline%3F"></a> What Exactly is a DevOps Pipeline?</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/What-is-DevOps-7qUdwvMMLC-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/What-is-DevOps-7qUdwvMMLC-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the DevOps Lifecycle" loading="lazy" decoding="async" src="https://flowfuse.com/img/What-is-DevOps-7qUdwvMMLC-1920.jpeg" width="1920" height="1049" /></picture>
<em>The DevOps Lifecycle illustrating the stages of development through to production</em></p>
<p>A DevOps pipeline is an process that helps developers move their code from development to production smoothly. In a pipeline, each step depends on the one before it, ensuring every update is properly tested and ready for use. The most common stages of a pipeline are:</p>
<ol>
<li><strong>Development:</strong> This is where the application flows are created. A level of testing and checking is carried out as a natural step of the development.</li>
<li><strong>Staging/Testing:</strong> In this stage, the code is deployed to a staging environment that closely mimics the live system. Here, the application goes through a level of QA and is tested in scenarios as close to real-world conditions as possible. This is stage designed to catch any issues that slipped through in the development stage.</li>
<li><strong>Production:</strong> When the flows are tested and confirmed in staging and are ready to be deployed, this stage can be operated to promote the flows to production.</li>
</ol>
<h2 id="how-to-create-devops-pipelines-for-node-red-deployments" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/#how-to-create-devops-pipelines-for-node-red-deployments"></a> How to Create DevOps Pipelines for Node-RED Deployments</h2>
<p>Creating DevOps pipelines manually for your Node-RED deployments can be time-consuming, expensive, and require considerable technical expertise. <strong>FlowFuse</strong> simplifies the creation of DevOps pipelines for Node-RED deployments.</p>
<p><a href="https://flowfuse.com/">FlowFuse</a> enhances collaboration, security, and scalability for your Node-RED applications, making the deployment and management of edge devices seamless. With a centralized platform and an intuitive visual interface, FlowFuse allows you to connect, collect, transform, and visualize data effortlessly.</p>
<h3 id="steps-to-create-a-devops-pipeline%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/#steps-to-create-a-devops-pipeline%3A"></a> Steps to Create a DevOps Pipeline:</h3>
<ol>
<li>Go to the <strong>FlowFuse platform</strong> and navigate to the application where your Node-RED instances are located. Ensure you have instances set up for all stages, including production devices or instances.</li>
<li>Switch to the <strong>DevOps Pipelines</strong> option from the top menu.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/devops-pipeline-option-in-apps-mZpkMgfTWr-1893.avif 1893w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/devops-pipeline-option-in-apps-mZpkMgfTWr-1893.webp 1893w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing option to switch to DevOps pipelines tab from top menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/devops-pipeline-option-in-apps-mZpkMgfTWr-1893.jpeg" width="1893" height="801" /></picture>
<em>Image showing option to switch to DevOps pipelines tab from top menu</em></p>
<ol start="3">
<li>Click the <strong>Add Pipeline</strong> button in the top right corner to create the pipeline.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/add-pipeline-button--MoBGnLl8O-1893.avif 1893w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/add-pipeline-button--MoBGnLl8O-1893.webp 1893w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the FlowFuse dashboard with the 'Add Pipeline' button highlighted at the top-right corner" loading="lazy" decoding="async" src="https://flowfuse.com/img/add-pipeline-button--MoBGnLl8O-1893.jpeg" width="1893" height="801" /></picture>
<em>Click the 'Add Pipeline' button to start creating your DevOps pipeline in FlowFuse</em></p>
<ol start="4">
<li>Enter the name for your pipeline and click <strong>Create Pipeline</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-to-create-pipeline-PbKcUCIDdW-584.avif 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-to-create-pipeline-PbKcUCIDdW-584.webp 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the pipeline creation form in FlowFuse, showing fields to enter the pipeline's name" loading="lazy" decoding="async" src="https://flowfuse.com/img/form-to-create-pipeline-PbKcUCIDdW-584.jpeg" width="584" height="252" /></picture>
<em>Fill out the form to give your pipeline a name</em></p>
<ol start="5">
<li>Next, you'll see an option to create stages by clicking <strong>Add Stage</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/button-to-add-stages-_L0Kyvr-A4-1641.avif 1641w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/button-to-add-stages-_L0Kyvr-A4-1641.webp 1641w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot highlighting the button to add stages within a DevOps pipeline in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/button-to-add-stages-_L0Kyvr-A4-1641.jpeg" width="1641" height="467" /></picture>
<em>Add stages to your pipeline for different deployment environments, such as development, testing, and production</em></p>
<ol start="6">
<li>In the window that opens, select the <strong>stage type</strong> based on whether it's an instance, device, or device group.</li>
<li>Enter the name for the stage in the <strong>Stage Name</strong> field.</li>
<li>Choose an instance, device, or device group for the stage.</li>
<li>Next, configure which action should be performed when this stage is pushed to the next:
<ul>
<li><strong>Create New Snapshot:</strong> Generates a new <a href="https://flowfuse.com/docs/user/high-availability/">Snapshot</a> using the current flows and settings.</li>
<li><strong>Use Latest Instance Snapshot:</strong> Uses the most recent existing snapshot of the instance. The deployment will fail if no snapshot exists.</li>
<li><strong>Prompt to Select Snapshot:</strong> Prompts at deploy time to select which snapshot from the source stage should be copied to the next stage.</li>
</ul>
</li>
<li>Check the option <strong>Deploy to Devices</strong> if you want changes to be deployed to all devices connected to this stage’s instance when the stage is deployed.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-to-create-configure-stages-3yQvbBGz3a-1521.avif 1521w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-to-create-configure-stages-3yQvbBGz3a-1521.webp 1521w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the stage configuration form with options to select instance types and configure actions." loading="lazy" decoding="async" src="https://flowfuse.com/img/form-to-create-configure-stages-3yQvbBGz3a-1521.jpeg" width="1521" height="717" /></picture>
<em>Configure each stage by selecting an instance, device, or device group, and define the deployment actions</em></p>
<p>Once you’ve created your initial stage, you can add more stages by following the same process. This flexibility allows you to tailor your DevOps pipeline to meet the specific needs of your Node-RED deployment.</p>
<p>For example, in development, you might have a Node-RED instance in the cloud to build your application. During staging, you could test the setup with a single device. Finally, in production, you can deploy the tested application to thousands of devices in a device group, saving time and ensuring smooth deployment at scale.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/devops-pipeline-_Yv3DOd_---800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="DevOps pipelines animation" loading="lazy" decoding="async" src="https://flowfuse.com/img/devops-pipeline-_Yv3DOd_---800.webp" width="800" height="449" /></picture>
<em>Image: DevOps animation demonstrating pipeline deployments.</em></p>
<h2 id="running-a-pipeline-stage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/#running-a-pipeline-stage"></a> Running a Pipeline Stage</h2>
<p>Once your pipeline is set up, you can run it to deploy your changes across each stage. Here's how:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/button-to-run-pipeline-QNCcJKO3q3-1641.avif 1641w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/button-to-run-pipeline-QNCcJKO3q3-1641.webp 1641w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the 'Run Pipeline' button in FlowFuse, allowing users to trigger the deployment process." loading="lazy" decoding="async" src="https://flowfuse.com/img/button-to-run-pipeline-QNCcJKO3q3-1641.jpeg" width="1641" height="581" /></picture>
<em>Click the 'Run Pipeline' button to initiate a pipeline deployment.</em></p>
<ol>
<li>
<p>Click the "Run Pipeline" button for the current stage to start the deployment. This button is available for all stages except the last one.</p>
</li>
<li>
<p>After clicking, the deployment automatically progresses to the next stage on the right. Since each pair of stages operates independently, you need to click the "Run" button for each stage to continue the deployment.</p>
</li>
</ol>
<p>Pressing the "Run Pipeline" button for the current stage creates a new snapshot that includes all settings, environment variables, and flows for that stage. This snapshot is then copied and deployed to the next stage, but any existing environment variable keys in the target stage will remain unchanged.</p>
<p>When creating a pipeline, you can include only one Device Group, and it must be in the final stage. This ensures all changes are fully tested and verified before reaching production, guaranteeing a safe and reliable deployment.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/how-to-build-automate-devops-pipelines-node-red-deployments/#conclusion"></a> Conclusion</h2>
<p>DevOps pipelines formalize deployment patterns, standardize testing, and streamline updates, minimizing errors and downtime. This clear process improves reliability and helps organizations adapt more easily thus ensuring smooth operation of critical applications in manufacturing and automotive environments.</p>
<div>
<p>Want to learn how FlowFuse can help you scale, manage, and optimize your industrial processes?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/book-demo/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20book%20demo&utm_term=high_intent&utm_content=Creating%20and%20Automating%20DevOps%20Pipelines%20for%20Node-RED%20in%20Industrial%20Environments">
Let’s Walk You Through It – Book a Demo Today
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/Using Snapshots for Version Control in Node-RED with FlowFuseEffortlessly manage and recover your Node-RED flows with snapshots in FlowFuse.2024-09-26T00:00:00ZSumit ShindeSteve McLaughlin<p>Version control is essential, especially when multiple people are working on the same Node-RED project. Without it, changes can easily overlap, or worse — accidental updates could break critical flows. FlowFuse solves this challenge with snapshots, allowing you to create backups of your flows, restore previous versions, and safeguard your project from unexpected issues.</p>
<!--more-->
<p>Let's look at how to use snapshots in FlowFuse to manage your Node-RED projects with confidence and prevent costly mistakes.</p>
<h2 id="what-is-version-control-and-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#what-is-version-control-and-snapshots"></a> What is Version Control and Snapshots</h2>
<p>Using <strong>version control</strong> for Node-RED flows can introduce complexity and effort, as it often requires frequent pushes of changes to keep everything in sync. This is especially challenging in environments where modifications occur rapidly and continuously.</p>
<p><strong>Snapshots</strong> simplify this process by providing point-in-time backups of your flows. In the context of Node-RED, snapshots automatically capture the state of your work, ensuring that you can quickly restore them if needed. It captures the following:</p>
<ul>
<li><strong>Flows</strong>: The flow, including all nodes and config nodes of your Node-RED flows.</li>
<li><strong>Credentials</strong>: Any sensitive information used within flows using config nodes.</li>
<li><strong>Environment Variables</strong>: Environment variables you have used or defined within that Node-RED instance.</li>
<li><strong>Packages</strong>: The packages you have installed, including 3rd party contribution nodes and Node.js packages.</li>
<li><strong>Runtime Settings</strong>: The configurations that govern the behavior of your Node-RED runtime.</li>
</ul>
<p>With snapshots in <a href="https://flowfuse.com/">FlowFuse</a>, you can focus on developing your projects while having the confidence that your work is protected.</p>
<h2 id="managing-snapshots-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#managing-snapshots-in-flowfuse"></a> Managing Snapshots in FlowFuse</h2>
<p>Creating snapshots in FlowFuse is straightforward and can be done in just a few steps.</p>
<h3 id="creating-snapshots-for-cloud-and-device-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#creating-snapshots-for-cloud-and-device-instances"></a> Creating Snapshots for Cloud and Device Instances</h3>
<p>Before we begin, it’s essential to understand the differences between a cloud instance and a device instance. For more information, refer to the <a href="https://flowfuse.com/docs/user/concepts/#what-is-the-difference-between-an-instance-and-a-device%3F">Documentation</a>.</p>
<p>Additionally, let’s discuss the two types of device assignments available in FlowFuse:</p>
<ul>
<li>Application Device
<ul>
<li>When a device is assigned to an instance, it can be considered as a mirror of the instance. In Fleet mode, it downloads and runs the target snapshot.</li>
</ul>
</li>
<li>Instance Device
<ul>
<li>When a device is assigned to an application, it can be considered as a standalone entity.</li>
</ul>
</li>
</ul>
<p>With that in mind, taking snapshots for an Application Device is a bit different from taking a snapshot for an Instance Device. For example, taking snapshots of an Application Device will have the same user experience as taking a snapshot of a cloud instance. However, since an Instance Device is typically closely coupled with the owner instance, there is a slightly different procedure. We will cover both below.</p>
<h4 id="creating-snapshots-for-cloud-instance-and-application-device" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#creating-snapshots-for-cloud-instance-and-application-device"></a> Creating Snapshots for Cloud Instance and Application Device</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/instances-option-in-sidebar-24vWzlWrHk-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/instances-option-in-sidebar-24vWzlWrHk-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the "Instances" option in the sidebar" loading="lazy" decoding="async" src="https://flowfuse.com/img/instances-option-in-sidebar-24vWzlWrHk-1919.jpeg" width="1919" height="825" /></picture>
<em>Image showing the "Instances" option in the sidebar</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/devices-option-in-the-sidebar-4ip4Uy4PYA-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/devices-option-in-the-sidebar-4ip4Uy4PYA-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the "Devices" option in the sidebar" loading="lazy" decoding="async" src="https://flowfuse.com/img/devices-option-in-the-sidebar-4ip4Uy4PYA-1919.jpeg" width="1919" height="822" /></picture>
<em>Image showing the "Devices" option in the sidebar</em></p>
<ol>
<li>Log in to your FlowFuse account and navigate to the instances by clicking on <strong>"Instances"</strong> in the sidebar, or if you want to create a snapshot for an Application Device, click on <strong>"Devices."</strong></li>
<li>Click on the instance or device you want to create a snapshot for. This will take you to the management interface, which includes different tabs for various settings.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/option-to-switch-to-snapshots-tab-kOzVRDW6Zv-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/option-to-switch-to-snapshots-tab-kOzVRDW6Zv-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the option to switch to the Snapshots tab in FlowFuse Cloud Instance." loading="lazy" decoding="async" src="https://flowfuse.com/img/option-to-switch-to-snapshots-tab-kOzVRDW6Zv-1919.jpeg" width="1919" height="694" /></picture>
<em>Image showing the option to switch to the Snapshots tab in FlowFuse Cloud Instance.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/application-device-snapshot-tab-ruhmRjYYKN-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/application-device-snapshot-tab-ruhmRjYYKN-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the option to switch to the Snapshots tab in FlowFuse Application Device." loading="lazy" decoding="async" src="https://flowfuse.com/img/application-device-snapshot-tab-ruhmRjYYKN-1919.jpeg" width="1919" height="558" /></picture>
<em>Image showing the option to switch to the Snapshots tab in FlowFuse Application Device.</em></p>
<ol start="3">
<li>Switch to the <strong>“Snapshots”</strong> tab by selecting it from the instance management options. Here, you will find options to create a new snapshot called <strong>"Create Snapshot"</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/button-to-create-snapshot-zUNt_2rZSb-1916.avif 1916w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/button-to-create-snapshot-zUNt_2rZSb-1916.webp 1916w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the button to create a snapshot in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/button-to-create-snapshot-zUNt_2rZSb-1916.jpeg" width="1916" height="822" /></picture>
<em>Image showing the button to create a snapshot in FlowFuse.</em></p>
<ol start="4">
<li>Click on the button to <strong>create snapshot</strong>. You will be prompted to enter a <strong>name and description</strong> for the snapshot, helping you identify it later. There’s also an option to set this snapshot as Device Target Snapshot — enable this checkbox if needed.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-to-give-snapshot-name-desc-sg8U7T_DdA-1377.avif 1377w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-to-give-snapshot-name-desc-sg8U7T_DdA-1377.webp 1377w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the form to provide a name and description for the snapshot." loading="lazy" decoding="async" src="https://flowfuse.com/img/form-to-give-snapshot-name-desc-sg8U7T_DdA-1377.jpeg" width="1377" height="621" /></picture>
<em>Image showing the form to provide a name and description for the snapshot.</em></p>
<ol start="5">
<li>After creating the snapshot, you will receive a confirmation message indicating that the snapshot has been successfully created.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/snapshot-in-the-list-OzKV1oeAWX-1643.avif 1643w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/snapshot-in-the-list-OzKV1oeAWX-1643.webp 1643w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing a list of created snapshots with details in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/snapshot-in-the-list-OzKV1oeAWX-1643.jpeg" width="1643" height="564" /></picture>
<em>Image showing a list of created snapshots with details in FlowFuse.</em></p>
<p>Once a snapshot is created, it will be visible in a list format. Each snapshot will display details such as the name, description, creator, and creation date, along with a three-dot icon.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/options-available-for-instance-3ocm0dfgmO-612.avif 612w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/options-available-for-instance-3ocm0dfgmO-612.webp 612w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing available options for managing snapshots in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/options-available-for-instance-3ocm0dfgmO-612.jpeg" width="612" height="431" /></picture>
<em>Image showing available options for managing snapshots in FlowFuse.</em></p>
<p>Clicking on the icon will open different options to manage and operate the snapshot.</p>
<h3 id="creating-snapshot-for-device-instances-when-assigned-to-cloud-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#creating-snapshot-for-device-instances-when-assigned-to-cloud-instance"></a> Creating Snapshot for Device Instances when assigned to Cloud Instance</h3>
<p>If the device is in fleet mode, it will be running the flows specified by the target snapshot, and there is typically no need to create a snapshot directly from the device. However, if the device is in developer mode, it may have been modified and may have different flows than those in the instances. In this case, you can take a snapshot directly from the device using the Developer Mode tab. Here’s how to do it:</p>
<ol>
<li>Go to <strong>Devices</strong> by clicking on the <strong>Devices</strong> option in the sidebar, and then click on the specific device.</li>
<li>Once clicked, a similar interface will open as with instances, allowing you to manage and monitor its settings and configuration. Ensure that the device has developer mode enabled.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/option-to-create-device-snapshot-zL0N0yv1U4-828.avif 828w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/option-to-create-device-snapshot-zL0N0yv1U4-828.webp 828w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Create Snapshot' option in the Developer mode tab" loading="lazy" decoding="async" src="https://flowfuse.com/img/option-to-create-device-snapshot-zL0N0yv1U4-828.jpeg" width="828" height="348" /></picture>
<em>Image showing the 'Create Snapshot' option in the Developer mode tab</em></p>
<ol start="3">
<li>Switch to the <strong>Developer Mode</strong> tab by clicking on the <strong>Developer Mode</strong> option at the top. In the Developer Mode section, you will see the <strong>Create Snapshot</strong> button. Click on it to create a snapshot, then enter the details such as the name and description. If needed, set it as the device target snapshot, and then click <strong>Create</strong>.</li>
</ol>
<h3 id="viewing-and-comparing-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#viewing-and-comparing-snapshots"></a> Viewing and Comparing Snapshots</h3>
<p>In the <strong>Snapshots</strong> tab, you can view all snapshots associated with your instance. FlowFuse also allows you to compare two snapshots, enabling you to track changes between different versions of your flows. This is especially useful for identifying specific changes that may have caused issues or to review the progress over time.</p>
<h4 id="viewing-a-snapshot%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#viewing-a-snapshot%3A"></a> Viewing a Snapshot:</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/view-snapshot-option-CEM6hzEgDH-435.avif 435w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/view-snapshot-option-CEM6hzEgDH-435.webp 435w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'View Snapshot' option in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/view-snapshot-option-CEM6hzEgDH-435.jpeg" width="435" height="335" /></picture>
<em>Image showing the 'View Snapshot' option in the menu</em></p>
<ol>
<li>Click on the three-dot icon next to the corresponding snapshot you want to view, then select <strong>View Snapshot</strong> from the menu.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/view-snapshot-window-39SIrOynzz-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/view-snapshot-window-39SIrOynzz-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the snapshot view window in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/view-snapshot-window-39SIrOynzz-1919.jpeg" width="1919" height="816" /></picture>
<em>Image showing the snapshot view window in FlowFuse.</em></p>
<ol start="2">
<li>After clicking, a new window will open displaying the entire flow for that snapshot. At the bottom right of this window, you'll find options to <strong>copy</strong> or <strong>download</strong> the flow.</li>
</ol>
<h4 id="comparing-snapshots%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#comparing-snapshots%3A"></a> Comparing Snapshots:</h4>
<p>If you have multiple snapshots and want to compare them, follow these steps:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compare-snapshot-option-PHEQSUTEEW-398.avif 398w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/compare-snapshot-option-PHEQSUTEEW-398.webp 398w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Compare Snapshot' option in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/compare-snapshot-option-PHEQSUTEEW-398.jpeg" width="398" height="334" /></picture>
<em>Image showing the 'Compare Snapshot' option in the menu</em></p>
<ol>
<li>Click on the three-dot icon next to the snapshot you want to compare, then choose <strong>Compare Snapshot</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/window-comparing-snapshots-699QPpqGft-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/window-comparing-snapshots-699QPpqGft-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the comparison window in FlowFuse, featuring a dropdown to select the instance to compare and a compare button." loading="lazy" decoding="async" src="https://flowfuse.com/img/window-comparing-snapshots-699QPpqGft-1919.jpeg" width="1919" height="823" /></picture>
<em>Image showing the comparison window in FlowFuse, featuring a dropdown to select the instance to compare and a compare button</em></p>
<ol start="2">
<li>In the newly opened window, select another snapshot from the dropdown to compare it with the one you chose.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/window-comparing-snapshots-2-zzoGR8p2rW-1919.avif 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/window-comparing-snapshots-2-zzoGR8p2rW-1919.webp 1919w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the comparison window for snapshots in FlowFuse, with differences highlighted in light purple color." loading="lazy" decoding="async" src="https://flowfuse.com/img/window-comparing-snapshots-2-zzoGR8p2rW-1919.jpeg" width="1919" height="822" /></picture>
<em>Image showing the comparison window for snapshots in FlowFuse.</em></p>
<ol start="3">
<li>The flows of both snapshots will be displayed side by side. At the top-right corner of the window, you will see two buttons, Use according to your preference:
<ul>
<li><strong>Prev</strong>: Navigate to the previous difference between the two snapshots.</li>
<li><strong>Next</strong>: Navigate to the next difference.</li>
</ul>
</li>
</ol>
<p>This comparison feature helps you easily identify changes, making it simple to spot issues or track flow modifications between versions.</p>
<h3 id="downloading%2C-uploading%2C-and-deploying-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#downloading%2C-uploading%2C-and-deploying-snapshots"></a> Downloading, Uploading, and Deploying Snapshots</h3>
<p>Created snapshots can be downloaded locally, providing you with a backup of your Node-RED flows. This backup can be uploaded to another instance or restored in case of emergencies.</p>
<h4 id="downloading-snapshot%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#downloading-snapshot%3A"></a> Downloading Snapshot:</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/download-snapshot-wUCqwIeoT1-389.avif 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/download-snapshot-wUCqwIeoT1-389.webp 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Download Snapshot' option in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/download-snapshot-wUCqwIeoT1-389.jpeg" width="389" height="335" /></picture>
<em>Image showing the 'Download Snapshot' option in the menu</em></p>
<ol>
<li>Click on the <strong>three-dot</strong> icon next to the corresponding snapshot, then select <strong>Download Snapshot</strong> from the menu.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/download-snapshot-prompt-xoJ-vLX4zJ-1409.avif 1409w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/download-snapshot-prompt-xoJ-vLX4zJ-1409.webp 1409w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the prompt to download a snapshot in FlowFuse, featuring a highlighted key and the download button." loading="lazy" decoding="async" src="https://flowfuse.com/img/download-snapshot-prompt-xoJ-vLX4zJ-1409.jpeg" width="1409" height="506" /></picture>
<em>Image showing the prompt to download a snapshot in FlowFuse, featuring a highlighted key and the download button</em></p>
<ol start="2">
<li>A <strong>Download Snapshot</strong> dialog will open. Note: If the flows contain sensitive values, an additional field for <strong>secret key</strong> will be displayed. This is used to encrypt the sensitive values in the snapshot. As a convenience, a random secret is auto generated however you should change this to something memorable as it will be needed when you later upload the snapshot.</li>
<li>Next, click the <strong>Download</strong> button located at the bottom right of the prompt to download the snapshot.</li>
</ol>
<h4 id="uploading-snapshots%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#uploading-snapshots%3A"></a> Uploading Snapshots:</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/button-upload-snapshot-ihcsJmdf57-1643.avif 1643w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/button-upload-snapshot-ihcsJmdf57-1643.webp 1643w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the button to upload a snapshot in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/button-upload-snapshot-ihcsJmdf57-1643.jpeg" width="1643" height="564" /></picture>
<em>Image showing the button to upload a snapshot in FlowFuse.</em></p>
<ol>
<li>In the <strong>Snapshots</strong> tab, click the <strong>Upload Snapshot</strong> button at the top-right corner.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/upload-prompt-cwGPzmZgV0-655.avif 655w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/upload-prompt-cwGPzmZgV0-655.webp 655w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the prompt to upload a snapshot in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/upload-prompt-cwGPzmZgV0-655.jpeg" width="655" height="590" /></picture>
<em>Image showing the prompt to upload a snapshot in FlowFuse.</em></p>
<ol start="2">
<li>Select the snapshot file from your local system. If a secret key was provided during the snapshot download, enter it. Otherwise, you will not be prompted for a key.</li>
<li>Click <strong>Upload</strong> to restore the snapshot to the desired instance.</li>
</ol>
<p>With this process, you can easily manage your snapshots across different environments, ensuring the safety and portability of your Node-RED flows.</p>
<h4 id="deploying-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#deploying-snapshots"></a> Deploying Snapshots</h4>
<p>Once you have learned how to download and upload snapshots, deploying them is quite straightforward.</p>
<p>To deploy a snapshot:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/deploy-snapshot-Sp5jKKPs59-410.avif 410w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/deploy-snapshot-Sp5jKKPs59-410.webp 410w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Deploy Snapshot' option in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/deploy-snapshot-Sp5jKKPs59-410.jpeg" width="410" height="344" /></picture>
<em>Image showing the 'Deploy Snapshot' option in the menu</em></p>
<ol>
<li>Click on the three-dot icon next to the corresponding snapshot and select <strong>Deploy Snapshot</strong> from the menu.</li>
<li>Next, you will be prompted to confirm the deployment. Review the details to ensure you are deploying the correct snapshot.</li>
<li>Click <strong>Confirm</strong> to proceed with the deployment.</li>
</ol>
<p>Upon successful deployment, the instance will restart with the state captured in the snapshot. This includes all flows, credentials, environment variables, NPM packages, and runtime settings as they were at the time of the snapshot. This process allows you to quickly recover from issues or revert to a previously stable state in your Node-RED instance.</p>
<h2 id="introducing-auto-snapshot" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#introducing-auto-snapshot"></a> Introducing Auto Snapshot</h2>
<p>Creating snapshots is crucial for documenting changes, and manually creating them gives users full control over when snapshots are taken. However, manually doing this can be time-consuming, and there is always the risk of forgetting to create one before making changes. To simplify this process, FlowFuse introduces the <strong>Auto Snapshot</strong> feature.</p>
<p>Auto Snapshots automatically create backups whenever you deploy changes, ensuring that your work is continuously backed up without requiring manual intervention. These snapshots are labeled as <strong>"Auto snapshot - yyyy-mm-dd hh:mm:ss"</strong> for easy identification.</p>
<p>This feature allows you to focus on developing your Node-RED flows with the assurance that your changes are securely saved. If necessary, you can disable Auto Snapshots for devices only from the <strong>Developer Mode</strong> tab. This can be helpful to avoid excessive data usage when a device is in the field or on a cellular connection, or to prevent reaching the limit of auto snapshots with unnecessary snapshots.</p>
<h3 id="disabling-auto-snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#disabling-auto-snapshots"></a> Disabling Auto Snapshots</h3>
<ol>
<li>Switch to the <strong>Developer Mode</strong> tab by clicking on the "Developer Mode" option at the top.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/enable-disable-auto-snapshot-f6uW6x8GDZ-1642.avif 1642w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/enable-disable-auto-snapshot-f6uW6x8GDZ-1642.webp 1642w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing option to enable/disable auto-snapshots for devices" loading="lazy" decoding="async" src="https://flowfuse.com/img/enable-disable-auto-snapshot-f6uW6x8GDZ-1642.jpeg" width="1642" height="417" /></picture>
<em>Image showing option to enable/disable auto-snapshots for devices</em></p>
<ol start="2">
<li>Once inside the Developer Mode tab, you will find the option to enable or disable the Auto Snapshot feature.</li>
</ol>
<p><strong>Note:</strong> A limit of 10 auto snapshots is maintained, with the oldest one being deleted when a new one is created. Also this feature is only available to <strong>Team</strong> and <strong>Enterprise</strong> users.</p>
<h2 id="setting-a-device-target-snapshot" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#setting-a-device-target-snapshot"></a> Setting a Device Target Snapshot</h2>
<p>When you set a device target snapshot, all devices associated with the instance will be restarted and updated to run that specified snapshot. This gives you full control over when devices receive the latest changes from your development instance.</p>
<p>With this feature, you can continue developing and testing your flows in FlowFuse without immediately impacting your devices. Once you're confident the flow is ready, you can push the changes to the devices by setting the target snapshot.</p>
<p>Device target snapshots can be assigned either during the creation of a snapshot or at a later time.</p>
<h3 id="to-set-the-already-creted-snapshot-as-a-device-target-snapshot%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#to-set-the-already-creted-snapshot-as-a-device-target-snapshot%3A"></a> To set the already creted Snapshot as a Device Target Snapshot:</h3>
<ol>
<li>Go to the <strong>Snapshots</strong> tab and click on the three-dot icon next to the desired snapshot.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/set-targe-snapshot-option-ZxsTXbRKE6-351.avif 351w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/set-targe-snapshot-option-ZxsTXbRKE6-351.webp 351w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the 'Set as Device Target' option in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/set-targe-snapshot-option-ZxsTXbRKE6-351.jpeg" width="351" height="425" /></picture>
<em>Image showing the 'Set as Device Target' option in the menu</em></p>
<ol start="2">
<li>Select <strong>Set as Device Target</strong> from the menu, prompt will open to conform then click on to the "Set Target".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/status-showing-the-target-snapshot-JB5oZPMLnD-1644.avif 1644w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/status-showing-the-target-snapshot-JB5oZPMLnD-1644.webp 1644w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the green status indicating how many devices have deployed this snapshot option in the menu." loading="lazy" decoding="async" src="https://flowfuse.com/img/status-showing-the-target-snapshot-JB5oZPMLnD-1644.jpeg" width="1644" height="415" /></picture>
<em>Image showing the green status indicating how many devices have deployed this snapshot option in the menu.</em></p>
<p>Once done you will be able to see the green mark in that snapshot showing on how much devices it is deployed on.</p>
<h2 id="common-mistakes-to-avoid" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#common-mistakes-to-avoid"></a> Common Mistakes to Avoid</h2>
<ol>
<li>
<p><strong>Neglecting Regular Backups</strong>: Don’t skip those snapshots! Regularly create backups, especially before making significant changes, moving devices through instances or applications, or disabling developer mode. Think of it as your safety net—ensuring you can always bounce back to a stable state if unexpected issues pop up.</p>
</li>
<li>
<p><strong>Overlooking Auto-Snapshot Limits</strong>: Did you know that your <strong>auto snapshots</strong> have limits? Be mindful of how many you can retain, as older <strong>auto snapshots</strong> will be automatically deleted. If you have important <strong>auto snapshots</strong>, either rename them to avoid automatic deletion or download and save them locally to keep them safe. Remember, manually created snapshots have no limits, so take advantage of that!</p>
</li>
<li>
<p><strong>Secret Key</strong>: The secret key used to encrypt your snapshot is crucial for when you later need to upload that snapshot. When downloading snapshots locally, securely store that key. Losing it could mean losing access to your snapshot and your ability to recover your work. Treat it like a password—keep it safe!</p>
</li>
<li>
<p><strong>Taking a Snapshots of the wrong thing</strong>: When a device is owned by an instance it typically runs the same flows however, if a device flows may have been modified directly in <strong>developer mode</strong> it will have different flows to the instance that owns it. In this case, before you move the device or switch it out of developer mode, it is recommended that you take a snapshot directly from the device itself. Direct device snapshots are performed on the <strong>Developer Mode</strong> tab of the device.</p>
</li>
<li>
<p><strong>Deploying Unverified Snapshots</strong>: A little caution goes a long way! Always review and verify the details of a snapshot before deploying it. Jumping into deployment without checking can lead to unexpected behavior or the loss of critical configurations. Take the time to ensure everything is in order.</p>
</li>
</ol>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/node-red-version-control-with-snapshots/#conclusion"></a> Conclusion</h3>
<p>Using snapshots in FlowFuse is an effective way to manage your Node-RED projects with confidence. By regularly creating snapshots, you can ensure that you always have a backup of your work, allowing you to quickly recover from mistakes or accidental changes.</p>
<div>
<p>Never lose your work again—keep every flow protected, collaborate effortlessly, and scale your Node-RED projects with confidence.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Using%20Snapshots%20for%20Version%20Control%20in%20Node-RED%20with%20FlowFuse">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/FlowFuse 2.9: Software Bill of Materials & Public Static AssetsLet's take a look at the new features and improvements in FlowFuse 2.92024-09-26T00:00:00ZJoe Pavitt<p>FlowFuse 2.9 bring improvements in application maintenance with the new "Software Bill of Materials" and in building full stack applications, with an iteration on the "Static Asset Service", by providing the option to "share" your assets assets publicly, making them consumable by external services and your Dashboards.</p>
<!--more-->
<h2 id="software-bill-of-materials" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#software-bill-of-materials"></a> Software Bill of Materials</h2>
<p>This release sees the introduction of a new feature for our Enterprise customers, the Software Bill of Materials (SBOM).</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-sbom-EgWZwDK6vZ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-sbom-EgWZwDK6vZ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Software Bill of Materials view in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-sbom-EgWZwDK6vZ-1920.jpeg" width="1920" height="1100" /></picture></p>
<p>This view is available for all of your Applications in FlowFuse, and provides a detailed breakdown of all packages running in your Node-RED instances within your applications, making it easier to trace down out of date packages and keep on top of the security and integrity of your applications.</p>
<p>In the above screenshot, we get a clear picture of the different versions of Node-RED that I'm running, as well as the different packages that are installed in each of my instances. Highlighting in particular that I have some out-of-date versions of <code>@flowfuse/node-red-dashboard</code>.</p>
<p>It's possible to expand the detail for each entry to see which instances are running the respective package, and consequently jump in to update them if appropriate.</p>
<h2 id="static-assets-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#static-assets-service"></a> Static Assets Service</h2>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/">Last release</a> we published the new "Static Assets Service". This new feature provided an easy way to upload files alongside your Node-RED instances, and make them easily accessible in your Node-RED Editor.</p>
<p>In this new release we've expanded on that feature, now allowing you to make those files publicly accessible via a URL, making them available for consumption in your Dashboards, or wherever else you may want to consume that content.</p>
<video controls="">
<source src="https://website-data.s3.eu-west-1.amazonaws.com/Assets+Service+Demo+-+Part+2.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<p>This unlocks a new set of possibilities for building full stack applications in FlowFuse, for examples, making it easy to customize branding in your Dashboard to match your company's branding, or to serve up images and other assets to your users.</p>
<h2 id="and-much-more..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#and-much-more..."></a> And Much More...</h2>
<p>For a full list of everything that went into our 2.9 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.9.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="coming-soon%3A-mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#coming-soon%3A-mqtt-broker"></a> Coming Soon: MQTT Broker</h2>
<p>FlowFuse already makes extensive use of MQTT under the covers. It provides the connection for our <a href="https://flowfuse.com/docs/user/projectnodes/">Project Nodes</a> that make it very simple to move data between your Node-RED instances.</p>
<p>We're now working on opening up the broker to allow teams to connect their MQTT devices to the platform without having to manage their own broker infrastructure.</p>
<p>This feature will allow you to run and manage your own MQTT Clients alongside your Node-RED instances, making it easier to build full-stack IoT applications with FlowFuse.</p>
<p>We do not have a precise release date for this just yet, but we're hoping to having this in for 2.10 or 2.11. Keep an eye out for more information on this in the coming weeks. If this feature is of interest to you, please do <a href="https://flowfuse.com/contact-us/">reach out</a> and help us shape it to make sure it meets your needs.</p>
<h2 id="try-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#try-flowfuse"></a> Try FlowFuse</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud.</p>
<p><a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.9, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<p>If you have an Enterprise license please make sure to review this <a href="https://flowfuse.com/changelog/2024/08/enterprise-license-update/">changelog entry</a></p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/flowfuse-release-2-9/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/How to Scrape Data from Websites Using Node-REDA step-by-step guide to leveraging Node-RED for efficient web scraping and automated data extraction.2024-09-16T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="what-is-web-scraping%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#what-is-web-scraping%3F"></a> What is Web Scraping?</h2>
<p>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.</p>
<p>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.</p>
<h2 id="how-does-web-scraping-works%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#how-does-web-scraping-works%3F"></a> How Does Web Scraping Works?</h2>
<p>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.</p>
<p>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.</p>
<h2 id="web-scrapping-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#web-scrapping-with-node-red"></a> Web scrapping with Node-RED</h2>
<p>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 <code>https://www.scrapethissite.com/pages/simple/.</code></p>
<h3 id="sending-requests-to-a-webpage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#sending-requests-to-a-webpage"></a> Sending Requests to a Webpage</h3>
<p>To start scraping data, follow these steps to send an HTTP GET request to the webpage:</p>
<ol>
<li>Drag the <strong>inject</strong> node onto the canvas. This node allows you to manually trigger the HTTP request or set it to fire at specific intervals.</li>
<li>Drag the <strong>http request</strong> node onto the canvas. Double-click it to configure and set the <strong>Method</strong> to <code>GET.</code> Enter the webpage URL you want to scrape (e.g., <code>https://www.scrapethissite.com/pages/simple/</code>).</li>
<li>Drag the <strong>debug</strong> node onto the canvas.</li>
<li>Connect the <strong>inject</strong> node's output to the input of the <strong>http request</strong> node and the <strong>http request</strong> node's output to the input of the <strong>debug</strong> node.</li>
<li>Click <strong>Deploy</strong> to save and deploy your flow.</li>
</ol>
<p>Once deployed, click the <strong>inject</strong> button. You will see the raw HTML printed in the debug panel.</p>
<h3 id="parsing-and-extracting-data-from-html" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#parsing-and-extracting-data-from-html"></a> Parsing and Extracting Data from HTML</h3>
<p>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.</p>
<h4 id="analyzing-html-structure" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#analyzing-html-structure"></a> Analyzing HTML Structure</h4>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/html-structer-of-target-website-OW5fyu8Vro-1645.avif 1645w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/html-structer-of-target-website-OW5fyu8Vro-1645.webp 1645w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the structure of the page and the data which we needed to extract" loading="lazy" decoding="async" src="https://flowfuse.com/img/html-structer-of-target-website-OW5fyu8Vro-1645.jpeg" width="1645" height="902" /></picture>
<em>Image showing the structure of the page and the data which we needed to extract</em></p>
<h4 id="using-node-red-to-extract-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#using-node-red-to-extract-data"></a> Using Node-RED to extract data</h4>
<ol>
<li>Drag the <strong>html</strong> node onto the canvas.</li>
<li>Double-click the <strong>html</strong> node and enter the selector <code>.countries</code> into the "Selector" field.</li>
<li>Set the output to "only the text of element" and keep other settings default.</li>
<li>Drag the <strong>debug</strong> node onto the canvas.</li>
<li>Connect the output of the <strong>http request</strong> node to the input of the <strong>html</strong> node and the output of the <strong>html</strong> node to the input of the <strong>debug</strong> node.</li>
<li>Click <strong>Deploy</strong> to save and deploy your flow.</li>
</ol>
<p>When you click the <strong>inject</strong> button, you will see the array containing the text content from each <code>.countries</code> 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.</p>
<h3 id="transforming-data-into-structured-objects" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#transforming-data-into-structured-objects"></a> Transforming Data into Structured Objects</h3>
<p>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 Assistant to generate the necessary function. For more details, refer to our <a href="https://www.linkedin.com/posts/flowfuse_flowfuse-nodered-automation-activity-7226171132796637184-vKKt/?utm_source=share&utm_medium=member_desktop">LinkedIn Post</a> for a quick guide. However, in this section, we will use a low-code approach to transform the data.</p>
<ol>
<li>
<p>Drag a <strong>Split</strong> node onto the canvas and connect it to the <strong>HTML</strong> node. This <strong>Split</strong> node will split the input array into individual string messages.</p>
</li>
<li>
<p>Drag a <strong>Change</strong> node onto the canvas and connect it to the <strong>Split</strong> node. Set <code>msg.name</code> to the following JSONata expression to extract the country name:</p>
<div style="position: relative" id="code-container-133">
<pre class="language-json"><code id="code-133" class="language-json">$trim($split(payload<span class="token punctuation">,</span> <span class="token string">"Capital: "</span>)<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>)</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-133" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Set <code>msg.payload</code> to the following Jsonata expression that will extract the capital and population from the string:</p>
<div style="position: relative" id="code-container-139">
<pre class="language-json"><code id="code-139" class="language-json">$split($split(payload<span class="token punctuation">,</span> <span class="token string">"Capital: "</span>)<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"Population: "</span>)</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-139" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag another <strong>Change</strong> node onto the canvas and connect it to the previous <strong>Change</strong> node. Set <code>msg.capital</code> to the following Jsonata expression to trim and extract the value of the capital from the previously split data array:</p>
<div style="position: relative" id="code-container-145">
<pre class="language-json"><code id="code-145" class="language-json">$trim(payload<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>)</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-145" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Set <code>msg.payload</code> to the following Jsonata expression to split the remaining string for area extraction:</p>
<div style="position: relative" id="code-container-151">
<pre class="language-json"><code id="code-151" class="language-json">$split(payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"Area (km2): "</span>)</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-151" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag another <strong>Change</strong> node onto the canvas and connect it to the <strong>Change</strong> node from the previous step. Set <code>msg.population</code> to the following Jsonata expression to trim and convert the population value to a number:</p>
<div style="position: relative" id="code-container-157">
<pre class="language-json"><code id="code-157" class="language-json">$number($trim(payload<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>))</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-157" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Set <code>msg.area</code> to the following Jsonata expression to trim and convert the area value to a number:</p>
<div style="position: relative" id="code-container-163">
<pre class="language-json"><code id="code-163" class="language-json">$number($trim(payload<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span>))</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-163" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag another <strong>Change</strong> node onto the canvas and connect it to the last <strong>Change</strong> node. Set <code>msg.payload</code> to the following JSON object:</p>
<div style="position: relative" id="code-container-169">
<pre class="language-json"><code id="code-169" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> name<span class="token punctuation">,</span><br /> <span class="token property">"capital"</span><span class="token operator">:</span> capital<span class="token punctuation">,</span><br /> <span class="token property">"population"</span><span class="token operator">:</span> population<span class="token punctuation">,</span><br /> <span class="token property">"area"</span><span class="token operator">:</span> area<br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-169" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Finally, drag a <strong>Join</strong> node onto the canvas and connect it to the previous <strong>Change</strong> node. This Join node will create an array of the objects we have created.</p>
</li>
</ol>
<p>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 <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-table.html">FlowFuse table widget</a>.</p>
<div id="nr-flow-154" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow154 = "\n[{\"id\":\"cc3c919ad9f93cc6\",\"type\":\"inject\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":\"https://www.scrapethissite.com/pages/simple/\",\"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\":[{\"t\":\"set\",\"p\":\"name\",\"pt\":\"msg\",\"to\":\"$trim($split(payload, \\\"Capital: \\\")[0])\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$split($split(payload, \\\"Capital: \\\")[1], \\\"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\":[{\"t\":\"set\",\"p\":\"capital\",\"pt\":\"msg\",\"to\":\"$trim(payload[0])\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$split(payload[1], \\\"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\":[{\"t\":\"set\",\"p\":\"population\",\"pt\":\"msg\",\"to\":\"$number($trim(payload[0]))\",\"tot\":\"jsonata\"},{\"t\":\"set\",\"p\":\"area\",\"pt\":\"msg\",\"to\":\"$number($trim(payload[1]))\",\"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\":\"/1\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"6be033291dd76b17\",\"order\":1,\"className\":\"\",\"visible\":false,\"disabled\":false},{\"id\":\"c385dfc590b1308d\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow154.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-154') })</script>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/webscrapping-result-62yi5k7oXA-1880.avif 1880w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/webscrapping-result-62yi5k7oXA-1880.webp 1880w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Left side: Image showing the countries table we created on the FlowFuse dashboard. Right side: The original webpage with countries." loading="lazy" decoding="async" src="https://flowfuse.com/img/webscrapping-result-62yi5k7oXA-1880.jpeg" width="1880" height="845" /></picture>
<em>Left side: Image showing the table we created on the FlowFuse dashboard. Right side: The original webpage with countries.</em></p>
<h2 id="legal-and-ethical-considerations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#legal-and-ethical-considerations"></a> Legal and Ethical Considerations</h2>
<p>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.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-scrape-web-data-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20Scrape%20Data%20from%20Websites%20Using%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/How to create and use Subflow in Node-REDA Practical Guide to Implementing Subflows in Node-RED for Efficient Workflow Management2024-09-13T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>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.</p>
<h2 id="what-exactly-are-subflows%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#what-exactly-are-subflows%3F"></a> What Exactly Are Subflows?</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/subflow-10YS8eysxX-1508.avif 1508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/subflow-10YS8eysxX-1508.webp 1508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing a Node-RED flow at the top selected for creating a Subflow, and the resulting Subflow at the bottom." loading="lazy" decoding="async" src="https://flowfuse.com/img/subflow-10YS8eysxX-1508.jpeg" width="1508" height="665" /></picture>
<em>Image showing a Node-RED flow at the top selected for creating a Subflow, and the resulting Subflow at the bottom.</em></p>
<p>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.</p>
<h2 id="creating-a-subflow-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#creating-a-subflow-in-node-red"></a> Creating a Subflow in Node-RED</h2>
<p>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.</p>
<p>To follow along, import the following flow into your Node-RED instance.</p>
<div id="nr-flow-152" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow152 = "\n[{\"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\":[]},{\"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\":[]},{\"id\":\"7e9e8e1af751bb92\",\"type\":\"inject\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":\"https://jsonplaceholder.typicode.com/todos\",\"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\":[{\"t\":\"btwn\",\"v\":\"200\",\"vt\":\"num\",\"v2\":\"299\",\"v2t\":\"num\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":2420,\"y\":240,\"wires\":[[\"612819f76617e5a8\"],[\"bad4bdb7e00b7a80\"]]},{\"id\":\"bad4bdb7e00b7a80\",\"type\":\"switch\",\"z\":\"380e37fed72e6885\",\"name\":\"if max retries\",\"property\":\"retry_counter\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"gte\",\"v\":\"10000\",\"vt\":\"num\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":2590,\"y\":280,\"wires\":[[\"132f4fdc40d55e89\"],[\"b4eca1de14599dd1\"]]},{\"id\":\"8899fea497064b8c\",\"type\":\"change\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"rules\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"retry_counter+1\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":2240,\"y\":240,\"wires\":[[\"3eb4ec6b71fc383b\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow152.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-152') })</script>
<h3 id="creating-subflow-of-selection" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#creating-subflow-of-selection"></a> Creating subflow of selection</h3>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/selecting-and-converting-subflow-EResh2NVNa-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing process of creating subflow from the selection" loading="lazy" decoding="async" src="https://flowfuse.com/img/selecting-and-converting-subflow-EResh2NVNa-800.webp" width="800" height="354" /></picture>
<em>Image showing process of creating subflow from the selection</em></p>
<ol>
<li>Select the flow you want to convert into a Subflow.</li>
<li>Open the main menu by clicking the top-right menu icon, and select "Selection to Subflow" under the Subflows option.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/subflow-showing-in-pallete-rNrbv0uojt-453.avif 453w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/subflow-showing-in-pallete-rNrbv0uojt-453.webp 453w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing subflow node added in the node palette" loading="lazy" decoding="async" src="https://flowfuse.com/img/subflow-showing-in-pallete-rNrbv0uojt-453.jpeg" width="453" height="591" /></picture>
<em>Image showing subflow node added in the node palette</em></p>
<p>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.</p>
<h3 id="adding-properties-to-the-subflow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#adding-properties-to-the-subflow"></a> Adding Properties to the Subflow</h3>
<ol>
<li>Double-click on the Subflow, then click on <strong>"Edit Subflow template"</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-template-option-yJuiE_eAEW-598.avif 598w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-template-option-yJuiE_eAEW-598.webp 598w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Editing the Subflow template by clicking on the 'Edit Subflow Template' option." loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-template-option-yJuiE_eAEW-598.jpeg" width="598" height="769" /></picture>
<em>Editing the Subflow template by clicking on the 'Edit Subflow Template' option.</em></p>
<ol start="2">
<li>A new flow tab for the Subflow will open. Click on <strong>"Edit Properties"</strong> in the top-left corner.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-properties-2Q70XJ13Hc-1241.avif 1241w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-properties-2Q70XJ13Hc-1241.webp 1241w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The edit properties button for a Subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-properties-2Q70XJ13Hc-1241.jpeg" width="1241" height="432" /></picture>
<em>The edit properties button for a Subflow</em></p>
<ol start="3">
<li>To add environment properties, click on the <strong>"+ add"</strong> button located at the bottom-left.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/add-env-Ovj9rwW4qR-608.avif 608w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/add-env-Ovj9rwW4qR-608.webp 608w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The 'Add' button for adding environment properties for subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/add-env-Ovj9rwW4qR-608.jpeg" width="608" height="772" /></picture>
<em>The 'Add' button for adding environment properties for subflow</em></p>
<ol start="4">
<li>In the field that opens, give the property a name and set its default value.</li>
<li>Once you have added all your properties, you can view a preview by switching to the <strong>"UI PREVIEW"</strong> tab.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-preivew-env-Y-CxQ6IuZQ-613.avif 613w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-preivew-env-Y-CxQ6IuZQ-613.webp 613w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="A preview of the Subflow environment properties" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-preivew-env-Y-CxQ6IuZQ-613.jpeg" width="613" height="383" /></picture>
<em>A preview of the Subflow environment properties</em></p>
<ol start="6">
<li>Click "Done" to save.</li>
</ol>
<h3 id="setting-added-environment-variables-in-the-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#setting-added-environment-variables-in-the-nodes"></a> Setting Added Environment Variables in the Nodes</h3>
<p>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.</p>
<ol>
<li>Double-click on the <strong>HTTP request</strong> node, set the environment variable as <code>${your_env_name}</code> into the URL feild, and click <strong>Done</strong> to save.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/http-request-url-env-adding-iBpDLvcKNL-626.avif 626w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/http-request-url-env-adding-iBpDLvcKNL-626.webp 626w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The URL field of an HTTP request node in Node-RED with an environment variable added." loading="lazy" decoding="async" src="https://flowfuse.com/img/http-request-url-env-adding-iBpDLvcKNL-626.jpeg" width="626" height="727" /></picture>
<em>The URL field of an HTTP request node in Node-RED with an environment variable added.</em></p>
<ol start="2">
<li>Next, double-click on the <strong>switch</strong> node named "if max retries," update the hardcoded max retry condition value to the environment variable you set for it, and click <strong>Done</strong> to save.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/max-retry-setting-R55ZZ9_rNI-631.avif 631w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/max-retry-setting-R55ZZ9_rNI-631.webp 631w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The switch node in Node-RED with a max retry condition set using an environment variable." loading="lazy" decoding="async" src="https://flowfuse.com/img/max-retry-setting-R55ZZ9_rNI-631.jpeg" width="631" height="718" /></picture>
<em>The switch node in Node-RED with a max retry condition set using an environment variable.</em></p>
<h3 id="managing-subflow-input-and-output-ports" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#managing-subflow-input-and-output-ports"></a> Managing Subflow Input and Output Ports</h3>
<p>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.</p>
<ol>
<li>In the <strong>Subflow</strong> tab, at the top, you will see an option for <strong>inputs</strong> with values 0 and 1. Click on <strong>1</strong> 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 <strong>change</strong> node.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/input-adding-subflow-61vmAUhyVr-1269.avif 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/input-adding-subflow-61vmAUhyVr-1269.webp 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Option to add input port for subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/input-adding-subflow-61vmAUhyVr-1269.jpeg" width="1269" height="325" /></picture>
<em>Option to add input port for subflow</em></p>
<ol start="2">
<li>Next, right after the <strong>inputs</strong> option, you will see an option for <strong>outputs</strong>. 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 <strong>switch</strong> nodes.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/output-adding-subflow-PvWIgbLroy-1269.avif 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/output-adding-subflow-PvWIgbLroy-1269.webp 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Option to add output ports for subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/output-adding-subflow-PvWIgbLroy-1269.jpeg" width="1269" height="325" /></picture>
<em>Option to add output ports for subflow</em></p>
<h3 id="adding-status-for-subflow-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#adding-status-for-subflow-nodes"></a> Adding Status for Subflow Nodes</h3>
<p>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:</p>
<ol>
<li>In the Subflow flow tab at the top, click on the <strong>Status</strong> 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 <code>msg.payload</code>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/status-adding-subflow-OJ_kUP6QhE-1269.avif 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/status-adding-subflow-OJ_kUP6QhE-1269.webp 1269w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Option to add status for subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/status-adding-subflow-OJ_kUP6QhE-1269.jpeg" width="1269" height="325" /></picture>
<em>Option to add status for subflow</em></p>
<p>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.</p>
<ol start="2">
<li>Drag two <strong>Change</strong> nodes onto the Canvas. Connect one Change node to the <strong>if success</strong> switch node's first output and set the <code>msg.payload</code> to <code>"completed"</code>. Connect the other Change node to the <strong>if max retries</strong> switch node's first output and set its <code>msg.payload</code> to <code>"retrying"</code>. Then, connect both Change nodes to the input of the Subflow status node.</li>
</ol>
<h3 id="customizing-the-appearance-of-a-subflow-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#customizing-the-appearance-of-a-subflow-node"></a> Customizing the Appearance of a Subflow Node</h3>
<p>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.</p>
<ol>
<li>In the Subflow flow tab, click on the <strong>"Edit Properties"</strong> option in the top-left corner and switch to the <strong>"Appearance"</strong> tab.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/customizing-apperance-mCgeUV_qG3-610.avif 610w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/customizing-apperance-mCgeUV_qG3-610.webp 610w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the apperance tab of subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/customizing-apperance-mCgeUV_qG3-610.jpeg" width="610" height="711" /></picture>
<em>Image showing the apperance tab of subflow</em></p>
<ol start="2">
<li>Select a category from the available categories or add a new one by clicking on <strong>"Add new"</strong>.</li>
<li>Choose a color for the Subflow node and select an icon.</li>
<li>Provide labels for the ports so that when someone hovers over the Subflow input or output ports, they can quickly understand their purpose.</li>
</ol>
<h3 id="adding-documentation-for-a-subflow-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#adding-documentation-for-a-subflow-node"></a> Adding Documentation for a Subflow Node</h3>
<p>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.</p>
<ol>
<li>In the Subflow flow tab, click on the <strong>"Edit Properties"</strong> option in the top-left corner and switch to the <strong>"Description"</strong> tab.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/documentation-tab-096avTaO5N-606.avif 606w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/documentation-tab-096avTaO5N-606.webp 606w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the description tab of subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/documentation-tab-096avTaO5N-606.jpeg" width="606" height="765" /></picture>
<em>Image showing the apperance tab of subflow</em></p>
<ol start="2">
<li>Enter the documentation content in markdown format that provides guidance on how to use the Subflow node effectively.</li>
<li>Click <strong>Done</strong> to save.</li>
</ol>
<p>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.</p>
<div id="nr-flow-153" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow153 = "\n[{\"id\":\"ea5436b592a86b90\",\"type\":\"subflow\",\"name\":\"API Retry \",\"info\":\"## hee\",\"category\":\"function\",\"in\":[{\"x\":50,\"y\":30,\"wires\":[{\"id\":\"8899fea497064b8c\"}]}],\"out\":[{\"x\":1140,\"y\":60,\"wires\":[{\"id\":\"3eb4ec6b71fc383b\",\"port\":0}]},{\"x\":1610,\"y\":120,\"wires\":[{\"id\":\"092547737a0cbee2\",\"port\":0}]}],\"env\":[{\"name\":\"URL\",\"type\":\"str\",\"value\":\"\"},{\"name\":\"MAX_RETRY\",\"type\":\"str\",\"value\":\"\"}],\"meta\":{},\"color\":\"#D7D7A0\",\"inputLabels\":[\"Trigger\"],\"outputLabels\":[\"API Response\",\"Max Retry Exeeded\"],\"icon\":\"node-red/white-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\":[],\"x\":550,\"y\":80,\"wires\":[[\"f523bb034db360a4\"]]},{\"id\":\"3eb4ec6b71fc383b\",\"type\":\"switch\",\"z\":\"ea5436b592a86b90\",\"name\":\"if success\",\"property\":\"statusCode\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"btwn\",\"v\":\"200\",\"vt\":\"num\",\"v2\":\"299\",\"v2t\":\"num\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":920,\"y\":80,\"wires\":[[],[\"bad4bdb7e00b7a80\"]]},{\"id\":\"bad4bdb7e00b7a80\",\"type\":\"switch\",\"z\":\"ea5436b592a86b90\",\"name\":\"if max retries\",\"property\":\"retry_counter\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"gte\",\"v\":\"MAX_RETRY\",\"vt\":\"env\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":1090,\"y\":120,\"wires\":[[\"092547737a0cbee2\"],[\"b4eca1de14599dd1\"]]},{\"id\":\"8899fea497064b8c\",\"type\":\"change\",\"z\":\"ea5436b592a86b90\",\"name\":\"\",\"rules\":[{\"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\":220,\"y\":80,\"wires\":[[\"b4eca1de14599dd1\"]]},{\"id\":\"f523bb034db360a4\",\"type\":\"change\",\"z\":\"ea5436b592a86b90\",\"name\":\"retry_counter++\",\"rules\":[{\"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\":[{\"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\":[]},{\"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\":[]},{\"id\":\"7e9e8e1af751bb92\",\"type\":\"inject\",\"z\":\"380e37fed72e6885\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[{\"name\":\"URL\",\"value\":\"https://jsonplaceholder.typicode.com/photosS\",\"type\":\"str\"},{\"name\":\"MAX_RETRY\",\"value\":\"10000\",\"type\":\"str\"}],\"x\":2160,\"y\":260,\"wires\":[[\"612819f76617e5a8\"],[\"132f4fdc40d55e89\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow153.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-153') })</script>
<p>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.</p>
<h3 id="benefits-of-using-subflows." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#benefits-of-using-subflows."></a> Benefits of using subflows.</h3>
<ul>
<li><strong>Modularity</strong>: Subflows allow you to group related nodes into a single, reusable unit, making complex flows easier to manage.</li>
<li><strong>Code Reuse</strong>: They help avoid duplicating similar logic across different parts of your flow, saving time and effort.</li>
<li><strong>Simplified Design</strong>: Subflows can simplify your main flow by hiding complexity within a single node.</li>
<li><strong>Easier Maintenance</strong>: Updating a Subflow automatically updates all instances where it is used, making maintenance quicker.</li>
</ul>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/09/how-to-use-subflow-in-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20create%20and%20use%20Subflow%20in%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/FlowFuse 2.8: Static File Service, LDAP Updates & MoreLet's take a look at the new features and improvements in FlowFuse 2.82024-08-29T00:00:00ZJoe Pavitt<p>FlowFuse 2.8 sees a new major feature introduced to the platform. The first iteration of the "Static Assets Service" is now available, allowing you to host and serve static files from your FlowFuse instance, giving you easy access to file storage for your applications, and seamless integration of those assets and files within Node-RED.</p>
<!--more-->
<h2 id="static-assets-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#static-assets-service"></a> Static Assets Service</h2>
<p>The Static Assets Service allows you to host and serve static files from your FlowFuse instance. This feature is particularly useful for applications that require file storage, such as images, videos, or static data sets.</p>
<p>With the Static Assets Service, you can upload files directly to your FlowFuse instance and access them from your Node-RED flows, like so:</p>
<video controls="">
<source src="https://website-data.s3.eu-west-1.amazonaws.com/Assets+Service+Demo.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<p>This is the first step in this new feature set, with more enhancements planned for future releases, whereby you'll also be able to configure access control to public HTTP endpoints for easy access in your Dashboards.</p>
<h2 id="ldap-service-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#ldap-service-improvements"></a> LDAP Service Improvements</h2>
<p>Team Membership in FlowFuse can now be automatically managed using LDAP groups. This means that you can now assign roles to LDAP groups, and when a user is added to that group in your LDAP server, they will automatically be assigned the respective role in FlowFuse.</p>
<p>This also extends to the management of FlowFuse Admin users, which was previously only supported for SAML SSO.</p>
<p>The Admin management feature is available for those running self-hosted FlowFuse.</p>
<h2 id="notifications-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#notifications-improvements"></a> Notifications Improvements</h2>
<p>Last release we introduced the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release#notifications-inbox">Notifications Inbox</a>, and in this release we're starting to expand on the notifications that you wil receive in FlowFuse, starting with alerts when your Node-RED instances and devices crash unexpectedly, and when instances start in "Safe Mode".</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/2-8-release-notifications-Ko5_flxgMs-817.avif 817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/2-8-release-notifications-Ko5_flxgMs-817.webp 817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing some example notifications to inform users of unexpected crashes" loading="lazy" decoding="async" src="https://flowfuse.com/img/2-8-release-notifications-Ko5_flxgMs-817.jpeg" width="817" height="792" /></picture>
<em>Screenshot showing some example notifications to inform users of unexpected crashes</em></p>
<p>We're also added filters to show "Read" notifications, and grouping together similar notifications so that it's easy to parse if you have many notifications of the same type.</p>
<h2 id="managing-devices-at-scale" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#managing-devices-at-scale"></a> Managing Devices at Scale</h2>
<p>As promised in the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release#bulk-device-actions">FlowFuse 2.7 Release</a>, we've expanded the actions you can take when working with many Devices at once, which now includes the ability to move Devices between Applications and Instances.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/2-8-release-bulk-move-q1_I_4JsOj-883.avif 883w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/2-8-release-bulk-move-q1_I_4JsOj-883.webp 883w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot to show the new "Move to Instance" and "Move to Application" bulk actions" loading="lazy" decoding="async" src="https://flowfuse.com/img/2-8-release-bulk-move-q1_I_4JsOj-883.jpeg" width="883" height="773" /></picture>
<em>Screenshot to show the new "Move to Instance" and "Move to Application" bulk actions</em></p>
<h2 id="stricter-approach-to-expired-licenses" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#stricter-approach-to-expired-licenses"></a> Stricter Approach to Expired Licenses</h2>
<p>We've made some changes to how FlowFuse handles expired licenses for self-hosted users.</p>
<p>If your license has expired, you will now be unable to access your FlowFuse instance until you renew your license. This is to ensure that you are always using the latest version of FlowFuse and have access to the latest features and security updates.</p>
<p>If you are currently running an older version of FlowFuse with an expired license please <a href="https://flowfuse.com/contact-us/">contact the Sales team</a> to discuss renewing your license before upgrading to FlowFuse 2.8. You can check your current license on the "Admin Settings" > "Overview" page.</p>
<h2 id="and-much-more..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#and-much-more..."></a> And Much More...</h2>
<p>For a full list of everything that went into our 2.8 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.8.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#try-it-out"></a> Try it out</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud: <a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.8, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<p>If you have an Enterprise license please make sure to review this <a href="https://flowfuse.com/changelog/2024/08/enterprise-license-update/">changelog entry</a></p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-8-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/New Layout, Widget and Gauges Now Available in FlowFuse DashboardOur latest update for FlowFuse Dashboard introduces a new layout type, Tabs, a new widget, Number Input, and two fresh gauges, Battery and Tank Level, along with much more.2024-08-28T00:00:00Z<p>At FlowFuse, we're constantly evolving to make sure your dashboard experience is seamless and efficient. This month, we're excited to introduce several new features that enhance your interaction with the platform. From organizing your data with Tabs to visualizing critical information with new Gauges, we've got you covered.</p>
<!--more-->
<h2 id="new-layout%3A-tabs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#new-layout%3A-tabs"></a> New Layout: Tabs</h2>
<p>We know that managing multiple data sources and visualizations can get overwhelming. To streamline your workflow, we've introduced a new layout: Tabs.</p>
<p>Tabs allow you to organize your widgets into separate, easily navigable sections, reducing clutter and making it easier to focus on specific datasets. Whether you're monitoring system performance, tracking KPIs, or managing IoT devices, Tabs will help you stay organized and efficient.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/layout-tab-dashboard-KgH43XaWKT-1484.avif 1484w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/layout-tab-dashboard-KgH43XaWKT-1484.webp 1484w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a tab layout in Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/layout-tab-dashboard-KgH43XaWKT-1484.jpeg" width="1484" height="798" /></picture>
<em>Screenshot showing a tab layout in Dashboard</em></p>
<h2 id="new-widget%3A-number-input" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#new-widget%3A-number-input"></a> New Widget: Number Input</h2>
<p>Introducing the Number Input widget — a versatile addition that allows users to input numerical values directly into the dashboard. Whether you're setting thresholds, configuring parameters, or simply inputting data, this widget makes it easy to adjust values with precision. It’s ideal for use cases where user interaction with numerical data is required, such as controlling devices or updating settings in real time.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-number-input-widget-Hw21b91szl-1732.avif 1732w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-number-input-widget-Hw21b91szl-1732.webp 1732w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a number input widget with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-number-input-widget-Hw21b91szl-1732.jpeg" width="1732" height="600" /></picture>
<em>Screenshot showing a number input widget with Dashboard</em></p>
<p>You can try the Number Input now in our <a href="https://dashboard-demos.flowfuse.cloud/dashboard/number-input">live Dashboard running on FlowFuse</a></p>
<h2 id="new-gauges" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#new-gauges"></a> New Gauges</h2>
<p>Data visualization is a cornerstone of any effective dashboard. We’re excited to introduce two new gauges designed to provide at-a-glance insights into your key metrics.</p>
<h3 id="battery-charge" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#battery-charge"></a> Battery Charge</h3>
<p>Monitoring battery levels is crucial for applications that rely on mobile or remote devices. Our new Battery Gauge provides a clear visual representation of battery status, allowing you to quickly assess power levels and take action if necessary. It’s perfect for IoT deployments, mobile sensors, or any system where battery life is a key consideration.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-gauge-battery-sfF1Nzvm6_-1024.avif 1024w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-gauge-battery-sfF1Nzvm6_-1024.webp 1024w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a battery gauge with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-gauge-battery-sfF1Nzvm6_-1024.jpeg" width="1024" height="526" /></picture>
<em>Screenshot showing a battery gauge with Dashboard</em></p>
<p>You can try the Tank Level now in our <a href="https://dashboard-demos.flowfuse.cloud/dashboard/gauge#battery-charge">live Dashboard running on FlowFuse</a></p>
<h3 id="tank-level" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#tank-level"></a> Tank Level</h3>
<p>Managing fluid levels in tanks is a common requirement across industries. The new Tank Level Gauge gives you a straightforward way to monitor liquid or gas levels in real time. Whether you're tracking water in a reservoir, fuel in a tank or any other fluid. this gauge provides the precision and clarity you need to maintain operational efficiency.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-gauge-tank-filled-rz-Tq04CHz-1432.avif 1432w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-gauge-tank-filled-rz-Tq04CHz-1432.webp 1432w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a partially filled tank level gauge with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-gauge-tank-filled-rz-Tq04CHz-1432.jpeg" width="1432" height="978" /></picture>
<em>Screenshot showing a partially filled tank level gauge with Dashboard</em></p>
<p>You can try the Tank Level now in our <a href="https://dashboard-demos.flowfuse.cloud/dashboard/gauge#tank-level">live Dashboard running on FlowFuse</a></p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#what-else-is-new%3F"></a> What else is new?</h2>
<p>You can find the full 1.15.0 Release Notes <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.15.0">here</a>.</p>
<p>Just to highlight a few, particularly valuable, updates and fixes:</p>
<ul>
<li>UI Switch - Introduced an "Indicator" mode and enhanced memory handling for better performance.</li>
<li>UI Table - Added a "Button" column type for improved interactivity.</li>
<li>UI Button - Color customisation now available, without needing to write overriding CSS.</li>
<li>Dynamic Properties - Added dynamic property support for <code>UI-Text-Input</code> widget.</li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/dashboard-new-layout-widgets-and-gauges/#what's-next%3F"></a> What's Next?</h2>
<p>Work has already begun on the next release, <code>1.16.0</code>, you can see what items we have queued up <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">here</a>, if you've got any feedback or suggestions, please do let us know, and feel free to open new issues on our <a href="https://github.com/FlowFuse/node-red-dashboard/issues">GitHub</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/Using MQTT Sparkplug B with Node-REDA Practical Guide to integrating MQTT Sparkplug B with Node-RED2024-08-22T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="what-is-mqtt-sparkplug%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#what-is-mqtt-sparkplug%3F"></a> What is MQTT Sparkplug?</h2>
<p><em>Sparkplug B</em> is an open-source specification governed by the <a href="https://www.eclipse.org/projects/efsp/">Eclipse Foundation Specification Process (EFSP)</a>. Its aims include defining an MQTT Topic Namespace and payload format that can be applied generically to the overall IIoT market sector in particular meeting the requirements of real-time SCADA/Control HMI solutions.</p>
<p>Take for example a factory where every machine sends data using different formats and non-standardized topic structures. Parsing this data is certainly possible but it introduces complexity, increases the risk of errors, and complicates maintenance. A consistent format can greatly simplify integration and reduce these challenges.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/with-plane-mqtt-iFJfo4UqK3-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/with-plane-mqtt-iFJfo4UqK3-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Manufacturing dashboard monitoring non-standardized data" loading="lazy" decoding="async" src="https://flowfuse.com/img/with-plane-mqtt-iFJfo4UqK3-1920.jpeg" width="1920" height="964" /></picture>
<em>Manufacturing dashboard monitoring non-standardized data</em></p>
<p>Sparkplug B simplifies things by standardizing the data format and structure, making it easier to integrate and manage data from different sources.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/with-sparkplug-2G3zmjmFfr-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/with-sparkplug-2G3zmjmFfr-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Manufacturing dashboard effectively monitoring data from devices using MQTT Sparkplug B's standardized data format." loading="lazy" decoding="async" src="https://flowfuse.com/img/with-sparkplug-2G3zmjmFfr-1920.jpeg" width="1920" height="998" /></picture>
<em>Manufacturing dashboard effectively monitoring data from devices using MQTT Sparkplug B's standardized data format</em></p>
<h2 id="understanding-mqtt-sparkplug-b-message-types%2C-payloads%2C-and-topic-structures" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#understanding-mqtt-sparkplug-b-message-types%2C-payloads%2C-and-topic-structures"></a> Understanding MQTT Sparkplug B Message Types, Payloads, and Topic Structures</h2>
<p>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.</p>
<p>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.</p>
<h3 id="types-of-sparkplug-b-messages" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#types-of-sparkplug-b-messages"></a> Types of Sparkplug B Messages</h3>
<p>Sparkplug B defines several message types to handle different aspects of communication:</p>
<ul>
<li><strong>NBIRTH</strong>: Sent by a node when it first connects to the system, announcing its presence and the metrics it will report. This type of message is published when an MQTT Sparkplug Out node or an EoN node connects to the broker.</li>
<li><strong>NDATA</strong>: Periodically sent by a node to update the values of its metrics.</li>
<li><strong>NDEATH</strong>: Indicates that an MQTT Sparkplug Out node or an EoN node has disconnected or is no longer available.</li>
<li><strong>NCMD</strong>: Allows for sending commands to a device for remote control or configuration.</li>
<li><strong>DBIRTH</strong>: Sent by a device upon connection to the MQTT broker, usually for initial setup or updates.</li>
<li><strong>DDEATH</strong>: Indicates that a device has been removed or is no longer available, complementing <code>NDEATH</code>.</li>
</ul>
<h3 id="key-components-of-a-sparkplug-b-payload" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#key-components-of-a-sparkplug-b-payload"></a> Key Components of a Sparkplug B Payload</h3>
<p>When dealing with Sparkplug B messages, such as <code>NBIRTH</code>, <code>NDATA</code>, <code>NDEATH</code>, <code>NCMD</code>, <code>DBIRTH</code>, <code>DDEATH</code> the payloads include several key components.</p>
<ul>
<li><strong>Timestamp</strong>: Every Sparkplug B payload includes a timestamp to record the exact time the data was captured. This is essential for understanding the timing and relevance of the data.</li>
<li><strong>Metrics</strong>: Metrics are data points within the payload, crucial for <code>NBIRTH</code>, <code>NDATA</code>, <code>DBIRTH</code>, and <code>DDEATH</code>, messages. Each metric includes:
<ul>
<li><strong>Name</strong>: The identifier for the metric (e.g., "Temperature").</li>
<li><strong>Alias</strong>: A shorthand identifier for metrics name to reduce the payload size.</li>
<li><strong>Timestamp</strong>: The time when the metric was sampled or updated.</li>
<li><strong>Datatype</strong>: The type of data (e.g., Integer, Float, String).</li>
<li><strong>Value</strong>: The actual data being communicated.</li>
<li><strong>Flags</strong>: Indicators such as <code>is_historical</code>, <code>is_transient</code>, and <code>is_null</code>.</li>
</ul>
</li>
<li><strong>Sequence Number</strong>: Each message includes a sequence number that increments with each new message. This helps detect any missing messages and ensures data consistency.</li>
</ul>
<h3 id="topic-naming-conventions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#topic-naming-conventions"></a> Topic Naming Conventions</h3>
<p>To effectively manage and route messages in Sparkplug B, it uses a well-structured topic namespace. This structured approach ensures that messages are organized logically and can be easily identified and processed by different systems</p>
<p>The topic namespace in Sparkplug B follows a specific format:</p>
<pre><code>spBv1.0/[Group ID]/[Message Type]/[EON Node ID]/[Device ID]
</code></pre>
<ul>
<li><strong>[Group ID]</strong>: Represents the group or application context.</li>
<li><strong>[Message Type]</strong>: Specifies the type of message (e.g., NBIRTH, NDATA).</li>
<li><strong>[EON Node ID]</strong>: Identifies the edge node or system.</li>
<li><strong>[Device ID]</strong>: Identifies the specific device (if applicable).</li>
</ul>
<h2 id="using-mqtt-sparkplug-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#using-mqtt-sparkplug-with-node-red"></a> Using MQTT Sparkplug with Node-RED</h2>
<p>In this section, we'll explore an example where Machine1 on the factory floor sends temperature and humidity data to an MQTT broker. We’ll cover how to integrate MQTT Sparkplug B into Node-RED and demonstrate basic operations such as sending and receiving data using MQTT Sparkplug B and more.</p>
<h3 id="prerequisite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#prerequisite"></a> Prerequisite</h3>
<p>Before you begin, ensure you have the following:</p>
<ul>
<li>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, If you haven’t created an account yet, <a href="https://app.flowfuse.com/account/create">sign up now</a>.</li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-mqtt-sparkplug-plus">node-red-contrib-mqtt-sparkplug-plus</a>: Install this Node-RED package for Sparkplug B support via palette manager.</li>
<li>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 <a href="https://flowfuse.com/docs/user/teambroker/">FlowFuse MQTT documentation</a>.</li>
</ul>
<h3 id="configuring-node-red-for-mqtt-sparkplug-b" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#configuring-node-red-for-mqtt-sparkplug-b"></a> Configuring Node-RED for MQTT Sparkplug B</h3>
<ol>
<li>Drag any mqtt sparkplug node onto the canvas.</li>
<li>Double-click the mqtt sparkplug node to open the configuration panel.</li>
<li>Click the "+" icon next to the "Broker" field. Enter your MQTT broker's host address (e.g., <code>broker.flowfuse.cloud</code>), specify the port number (e.g., <code>1883</code> for unencrypted or <code>8883</code> 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).</li>
<li>Switch to the Sparkplug tab by clicking the Sparkplug option in the top-right corner.</li>
<li>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.</li>
<li>Click "Add" to save the configuration.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-broker-config-9tcGbuDQFk-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-broker-config-9tcGbuDQFk-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the configuration of Sparkplug broker config node" alt="Screenshot showing the configuration of Sparkplug broker config node" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-broker-config-9tcGbuDQFk-650.jpeg" width="650" height="630" /></picture><br />
<em>Screenshot showing the configuration of Sparkplug broker config node</em></p>
<h3 id="sending-data-to-mqtt-with-sparkplug-b" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#sending-data-to-mqtt-with-sparkplug-b"></a> Sending Data to MQTT with Sparkplug B</h3>
<ol>
<li>
<p>Drag the inject node onto the canvas. Set the <code>msg.payload</code> 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:</p>
<div style="position: relative" id="code-container-231">
<pre class="language-json"><code id="code-231" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"metrics"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"sensor/temperature"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> $random() * <span class="token number">100</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"sensor/humidity"</span><span class="token punctuation">,</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> $random() * <span class="token number">100</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-231" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Drag the mqtt sparkplug device node onto the canvas.</p>
</li>
<li>
<p>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.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-sparkplug-device-node-ND0ohLfzhy-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-sparkplug-device-node-ND0ohLfzhy-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Sparkplug Device node configuration and the 'Add' button for defining metrics" alt="Screenshot showing the Sparkplug Device node configuration and the "Add" button for defining metrics" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-sparkplug-device-node-ND0ohLfzhy-650.jpeg" width="650" height="643" /></picture><br />
<em>Screenshot showing the Sparkplug Device node configuration and the "Add" button for defining metrics</em></p>
<ol start="4">
<li>
<p>Switch to the Advanced tab by clicking the "Advanced" option at the top-right.</p>
</li>
<li>
<p>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 <code>DBIRTH</code> message when the device node connects, but an <code>NBIRTH</code> message will be sent successful connection of mqtt sparkplug out node if you are using.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-spark-device-advance-k1Nl0XvyEb-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-spark-device-advance-k1Nl0XvyEb-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Sparkplug Device node configuration and the 'Add' button for defining metrics" alt="Screenshot showing the Sparkplug Device node configuration and the "Add" button for defining metrics" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-spark-device-advance-k1Nl0XvyEb-650.jpeg" width="650" height="412" /></picture><br />
<em>Screenshot showing the Sparkplug Device node configuration and the "Add" button for defining metrics</em></p>
<ol start="6">
<li>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.</li>
<li>Connect the inject node's output to the mqtt sparkplug device node's input.</li>
<li>Deploy the flow by clicking the top-right "Deploy" button.</li>
</ol>
<p>Once you deploy the flow and all devices connect to the MQTT broker, the system automatically send a <code>DBIRTH</code> message as soon as each device within the node connects, signalling that the device is ready for data transmission.</p>
<div id="nr-flow-146" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow146 = "\n[{\"id\":\"f2864f2b830e3590\",\"type\":\"mqtt sparkplug device\",\"z\":\"239c9025714089d3\",\"name\":\"Machine1\",\"metrics\":{\"sensor/temperature\":{\"dataType\":\"Float\",\"name\":\"sensor/temperature\"},\"sensor/humidity\":{\"dataType\":\"Float\",\"name\":\"sensor/humidity\"}},\"broker\":\"0d831bd9ba588536\",\"birthImmediately\":true,\"bufferDevice\":false,\"x\":380,\"y\":320,\"wires\":[[\"3dfc9b74f5e36bec\"]]},{\"id\":\"90cc413f58871fc1\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send Metrics\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"{ \\\"metrics\\\": [ { \\\"name\\\": \\\"sensor/temperature\\\", \\\"value\\\": $random()*100 }, { \\\"name\\\": \\\"sensor/humidity\\\", \\\"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\":[]},{\"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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow146.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-146') })</script>
<h3 id="receiving-data-from-mqtt-with-sparkplug-b" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#receiving-data-from-mqtt-with-sparkplug-b"></a> Receiving Data from MQTT with Sparkplug B</h3>
<ol>
<li>Drag the mqtt sparkplug in node onto the canvas.</li>
<li>Double-click the node and configure the broker settings.</li>
<li>Enter the topic in the "Topic" field in the format <code>namespace/group_id/message_type/edge_node_id/[device_id]</code>. Use <code>DDATA</code> for receiving metrics you are sending using device node or a wildcard like <code>spBv1.0/group_id/+/+/[device_id]</code> to listen to all message types from a specific device.</li>
<li>Select the desired "QoS" level.</li>
<li>Drag a debug node onto the canvas.</li>
<li>Connect the mqtt sparkplug in node’s output to the debug node’s input.</li>
<li>Click "Deploy" to save and run the flow.</li>
</ol>
<p>Now you will be able to see the <code>DBIRTH</code>, and <code>DDATA</code> messages printed on the debug panel.</p>
<div id="nr-flow-147" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow147 = "\n[{\"id\":\"a98c49d80bb5c4ee\",\"type\":\"mqtt sparkplug in\",\"z\":\"239c9025714089d3\",\"name\":\"\",\"topic\":\"spBv1.0/My Devices/DDATA/Node-RED/Machine1\",\"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\":[]},{\"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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow147.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-147') })</script>
<h3 id="sending-commands-for-devices-and-eon-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#sending-commands-for-devices-and-eon-nodes"></a> Sending Commands for devices and EoN nodes</h3>
<p>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.</p>
<ol>
<li>Drag inject node onto the canvas.</li>
<li>Set the <code>msg.command</code> 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:</li>
</ol>
<div style="position: relative" id="code-container-345">
<pre class="language-json"><code id="code-345" class="language-json"> <span class="token punctuation">{</span><br /> <span class="token property">"device"</span> <span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"death"</span> <span class="token operator">:</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-345" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Alternatively, to send a command that triggers a device's rebirth, use:</p>
<div style="position: relative" id="code-container-349">
<pre class="language-json"><code id="code-349" class="language-json"> <span class="token punctuation">{</span><br /> <span class="token property">"device"</span> <span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"rebirth"</span> <span class="token operator">:</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-349" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the output of the inject node to the input of the relevant mqtt sparkplug device node.</li>
<li>Deploy the flow by clicking the Deploy button at the top-right of the Node-RED interface.</li>
<li>Click the inject node’s button to send the command.</li>
</ol>
<p>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 <a href="https://flows.nodered.org/node/node-red-contrib-mqtt-sparkplug-plus">MQTT Sparkplug nodes documentation</a>.</p>
<div id="nr-flow-148" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow148 = "\n[{\"id\":\"f2864f2b830e3590\",\"type\":\"mqtt sparkplug device\",\"z\":\"239c9025714089d3\",\"name\":\"Machine1\",\"metrics\":{\"sensor/temperature\":{\"dataType\":\"Float\"},\"sensor/humidity\":{\"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\":[{\"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\":[]},{\"id\":\"915ca0772eebee04\",\"type\":\"inject\",\"z\":\"239c9025714089d3\",\"name\":\"Send rebirth command\",\"props\":[{\"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\":[{\"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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow148.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-148') })</script>
<p>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.</p>
<div id="nr-flow-149" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow149 = "\n[{\"id\":\"bbe3765e67eed956\",\"type\":\"mqtt sparkplug in\",\"z\":\"f098830cc10afc2f\",\"name\":\"\",\"topic\":\"spBv1.0/+/+/#\",\"qos\":\"2\",\"broker\":\"0d831bd9ba588536\",\"x\":150,\"y\":100,\"wires\":[[\"d45ff4446380beaa\"]]},{\"id\":\"3b2b9788c51d5c3b\",\"type\":\"mqtt sparkplug out\",\"z\":\"f098830cc10afc2f\",\"name\":\"\",\"topic\":\"spBv1.0/My Devices/NDATA/Node-Red\",\"qos\":\"\",\"retain\":\"\",\"broker\":\"0d831bd9ba588536\",\"x\":510,\"y\":200,\"wires\":[]},{\"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\":[]},{\"id\":\"dc73048fd385783a\",\"type\":\"inject\",\"z\":\"f098830cc10afc2f\",\"name\":\"Send Metrics\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"{ \\\"metrics\\\": [ { \\\"name\\\": \\\"sensor/temperature\\\", \\\"value\\\": $random(), \\\"type\\\": \\\"Float\\\" }, { \\\"name\\\": \\\"sensor/humidity\\\", \\\"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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow149.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-149') })</script>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-650.avif 650w, https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-650.webp 650w, https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-650.jpeg 650w, https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Images of some Sparkplug messages printed on debug panel" alt=""Images of some Sparkplug messages printed on debug panel"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sparkplug-messages-MhEnV70vcP-650.jpeg" width="1300" height="574" /></picture>
<em>Images of some Sparkplug messages printed on debug panel</em></p>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/using-mqtt-sparkplugb-with-node-red/#up-next"></a> Up Next</h2>
<ul>
<li><a href="https://flowfuse.com/node-red/protocol/mqtt/">Using MQTT With Node-RED</a>: This guide provides a basic overview of how to integrate MQTT with Node-RED, offering a foundational understanding for beginners who are just starting with this protocol.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">How to Use MQTT With Node-RED</a>: This comprehensive guide delves into the detailed steps and advanced techniques for effectively using MQTT within Node-RED, including practical examples to help you apply these concepts in real-world scenarios.</li>
</ul>
<div>
<p>Enhance your Node-RED experience with FlowFuse! Gain better scalability, streamlined deployments, and improved team collaboration—all while optimizing MQTT Sparkplug B integration for efficient data exchange.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Using%20MQTT%20Sparkplug%20B%20with%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/Monitoring and Optimizing Node-RED Flows with Open Telemetry.Integrating Open Telemetry with Node-RED for Efficient Distributed Tracing2024-08-15T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>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.</p>
<h2 id="what-is-distributed-tracing-and-how-does-open-telemetry-help%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#what-is-distributed-tracing-and-how-does-open-telemetry-help%3F"></a> What is Distributed Tracing and How Does Open Telemetry Help?</h2>
<p>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.</p>
<h3 id="what-is-opentelemetry%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#what-is-opentelemetry%3F"></a> What is OpenTelemetry?</h3>
<p>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.</p>
<p>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.</p>
<h2 id="tracing-in-node-red-flows-using-opentelemetry" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#tracing-in-node-red-flows-using-opentelemetry"></a> Tracing in Node-RED Flows using Opentelemetry</h2>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-150" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow150 = "\n[{\"id\":\"78e4a1255f9d0ad1\",\"type\":\"group\",\"z\":\"45e56b4089cada94\",\"name\":\"\",\"style\":{\"label\":true},\"nodes\":[\"636ac7b4d798a5c4\",\"ac87db82c2ab66f5\",\"c8a1749629359031\",\"c0d30281031f07db\"],\"x\":34,\"y\":139,\"w\":852,\"h\":82},{\"id\":\"636ac7b4d798a5c4\",\"type\":\"inject\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"Temperature sensor\",\"props\":[{\"p\":\"payload\"}],\"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\":[]},{\"id\":\"c8a1749629359031\",\"type\":\"change\",\"z\":\"45e56b4089cada94\",\"g\":\"78e4a1255f9d0ad1\",\"name\":\"Kelvin to Celsius\",\"rules\":[{\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow150.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-150') })</script>
<p>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</p>
<h3 id="prerequisite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#prerequisite"></a> Prerequisite</h3>
<p>Before you start, ensure you have the following:</p>
<ul>
<li><a href="https://flows.nodered.org/node/node-red-contrib-opentelemetry">node-red-contrib-opentelemetry</a> : Install this Node-RED module via the Node-RED Palette Manager.</li>
<li>Open Telemetry exporter: Set up an Open Telemetry exporter to send trace data to a backend. For details on available exporters, visit <a href="https://opentelemetry.io/docs/instrumentation/js/exporters/">Open Telemetry Exporters</a>. For this guide, I have set up the <a href="https://jaegertracing.io/">Jaeger</a>.</li>
</ul>
<h3 id="setting-open-telemetry-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#setting-open-telemetry-in-node-red"></a> Setting Open Telemetry in Node-RED</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opentelmetry-node-B4XN8XYSag-609.avif 609w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opentelmetry-node-B4XN8XYSag-609.webp 609w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the configuration of opentelmetry node" alt=""Screenshot showing the configuration of opentelmetry node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/opentelmetry-node-B4XN8XYSag-609.jpeg" width="609" height="787" /></picture>
<em>Screenshot showing the configuration of opentelmetry node</em></p>
<ol>
<li>Drag an <code>OTEL</code> node onto the canvas.</li>
<li>Double-click on the node and set the URL to your exporter endpoint (e.g., <code>http://localhost:4318/v1/traces</code> 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).</li>
<li>In the Ignore field, add the names of nodes you want to exclude from Open Telemetry tracing.</li>
<li>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.</li>
<li>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.</li>
<li>Now deploy the flow by clicking on the top-right deploy button.</li>
</ol>
<p>Once the flow is deployed, Open Telemetry will start collecting and sending trace data to your specified exporter.</p>
<h3 id="monitoring-performance-using-the-exporter-web-ui" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#monitoring-performance-using-the-exporter-web-ui"></a> Monitoring Performance Using the Exporter Web UI</h3>
<p>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.</p>
<ol>
<li>Open the Jaeger web UI in your browser. By default, it will be available at <code>http://localhost:16686/</code>.</li>
<li>Navigate to the "Search" by clicking on the "Search" option at the top.</li>
<li>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.</li>
<li>To monitor and find issues, select the desired trace and click on the "Find Trace" button. Click on the first trace to examine it.</li>
</ol>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/before-j6LyeBaSEi-650.avif 650w, https://flowfuse.com/img/before-j6LyeBaSEi-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/before-j6LyeBaSEi-650.webp 650w, https://flowfuse.com/img/before-j6LyeBaSEi-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/before-j6LyeBaSEi-650.jpeg 650w, https://flowfuse.com/img/before-j6LyeBaSEi-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the total duration taken by the flow" alt=""Image showing the total duration taken by the flow"" loading="lazy" decoding="async" src="https://flowfuse.com/img/before-j6LyeBaSEi-650.jpeg" width="1300" height="573" /></picture>
<em>Image showing the tototal duration taken by the flow</em></p>
<p>Since the issue was identified with the delay node, let's remove that delay node.</p>
<div id="nr-flow-151" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow151 = "\n[{\"id\":\"636ac7b4d798a5c4\",\"type\":\"inject\",\"z\":\"350fb9fbb98012be\",\"name\":\"Temperature sensor\",\"props\":[{\"p\":\"payload\"}],\"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\":[]},{\"id\":\"c8a1749629359031\",\"type\":\"change\",\"z\":\"350fb9fbb98012be\",\"name\":\"Kelvin to Celsius\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload - 273.15\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":420,\"y\":100,\"wires\":[[\"ac87db82c2ab66f5\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow151.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-151') })</script>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/after-V0mTieir3R-650.avif 650w, https://flowfuse.com/img/after-V0mTieir3R-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/after-V0mTieir3R-650.webp 650w, https://flowfuse.com/img/after-V0mTieir3R-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/after-V0mTieir3R-650.jpeg 650w, https://flowfuse.com/img/after-V0mTieir3R-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the total duration taken by the flow after fixing the issue" alt=""Image showing the total duration taken by the flow after fixing the issue"" loading="lazy" decoding="async" src="https://flowfuse.com/img/after-V0mTieir3R-650.jpeg" width="1300" height="573" /></picture>
<em>Image showing the total duration taken by the flow after fixing the issue</em></p>
<p>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.</p>
<h2 id="enhancing-monitoring-and-optimization-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#enhancing-monitoring-and-optimization-with-flowfuse"></a> Enhancing Monitoring and Optimization with FlowFuse</h2>
<p>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.</p>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Monitoring%20and%20Optimizing%20Node-RED%20Flows%20with%20Open%20Telemetry">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opentelemetry-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/Bridging OPC UA Data to MQTT with Node-REDConnecting OPC UA Data Streams to MQTT Brokers for Enhanced IoT Communication and Monitoring2024-08-13T00:00:00ZSumit Shinde<p>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?</p>
<!--more-->
<p>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.</p>
<h3 id="why-bridge-opc-ua-to-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#why-bridge-opc-ua-to-mqtt"></a> Why Bridge OPC UA to MQTT</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-ua-to-mqtt-RpOtJjqMHg-1803.avif 1803w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-ua-to-mqtt-RpOtJjqMHg-1803.webp 1803w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Diagram showing the data flow when bridging OPC UA to MQTT to enable communication between non-OPC UA compatible systems and devices" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-ua-to-mqtt-RpOtJjqMHg-1803.jpeg" width="1803" height="957" /></picture>
<em>Diagram showing the data flow when bridging OPC UA to MQTT to enable communication between non-OPC UA compatible systems and devices.</em></p>
<p>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.</p>
<p>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.</p>
<p><strong>Node-RED</strong> 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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/">Building on FlowFuse: Remote Device Monitoring</a>.</p>
<h2 id="bridging-opc-ua-data-to-mqtt-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#bridging-opc-ua-data-to-mqtt-with-node-red"></a> Bridging OPC UA Data to MQTT with Node-RED</h2>
<p>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.</p>
<h3 id="prerequisite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#prerequisite"></a> Prerequisite</h3>
<ul>
<li>
<p>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 <a href="https://prosysopc.com/products/opc-ua-simulation-server/">here</a>.</p>
</li>
<li>
<p>FlowFuse Account: A FlowFuse account lets you quickly create, deploy, and manage Node-RED instances in the cloud. <a href="https://app.flowfuse.com/account/create?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">sign up now</a>.</p>
</li>
<li>
<p><a href="https://flows.nodered.org/node/node-red-contrib-opcua">node-red-contrib-opcua</a>: install the node-red contrib package that will enable integration of opcua in Node-RED.</p>
</li>
<li>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">FlowFuse's MQTT Broker Announcement</a>.</p>
</li>
</ul>
<h3 id="retrieving-data-from-the-opc-ua-server" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#retrieving-data-from-the-opc-ua-server"></a> Retrieving Data from the OPC UA Server</h3>
<p>To begin retrieving data from your OPC UA server using Node-RED, follow these steps:</p>
<ol>
<li>Drag the <strong>inject</strong> node onto the canvas.</li>
<li>Drag the <strong>change</strong> node onto the canvas and double-click on the node to open its configuration settings. Set the <code>msg.topic</code> to the node ID and datatype of the property you wish to read.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.avif 1901w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.webp 1901w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="(Left) Image of the Change node setting the 'msg.topic' to retrieve the cycle time data and (Right) the OPC UA Prosys interface." loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.jpeg" width="1901" height="816" /></picture>
<em>(Left) Image of the Change node setting the 'msg.topic' to retrieve the cycle time data and (Right) the OPC UA Prosys interface.</em></p>
<ol start="2">
<li>Drag the <strong>OpcUa-Client</strong> 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."</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-ua-config-wBckjrgbGC-560.avif 560w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-ua-config-wBckjrgbGC-560.webp 560w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring opc-ua client node with the opc ua server endpoint" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-ua-config-wBckjrgbGC-560.jpeg" width="560" height="777" /></picture>
<em>Configuring opc-ua node with the opc ua server endpoint</em></p>
<ol start="3">
<li>In the <strong>OpcUa-Client</strong> node settings, select the action type as "READ." This instructs Node-RED to read data from the OPC UA server.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-ua-config2-mys6z6sawi-556.avif 556w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-ua-config2-mys6z6sawi-556.webp 556w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring OpcUa-Client node to select the read operation" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-ua-config2-mys6z6sawi-556.jpeg" width="556" height="771" /></picture>
<em>Configuring OpcUa-Client node to select the read operation</em></p>
<ol start="4">
<li>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.</li>
<li>Drag the <strong>debug</strong> node onto the canvas. The output will help you verify the data retrieved from the OPC UA server.</li>
<li>Connect the output of the <strong>inject</strong> node to the input of the <strong>change</strong> node and the output of the <strong>change</strong> node to the input of the <strong>OpcUa-Client</strong> node. Then, connect the output of the <strong>OpcUa-Client</strong> node to the input of the <strong>debug</strong> node. This setup ensures that when the <strong>inject</strong> node triggers, it sends data to the <strong>OpcUa-Client</strong> node, and the results are displayed in the Debug node.</li>
<li>Deploy the flow by clicking the "Deploy" button in the top right corner. To test the setup, press the Inject button.</li>
</ol>
<p>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.</p>
<h3 id="transforming-and-aggregating-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#transforming-and-aggregating-data"></a> Transforming and Aggregating Data</h3>
<p>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.</p>
<ol>
<li>Drag the <strong>change</strong> node onto the canvas.</li>
<li>Double-click on the node and set <code>msg.topic</code> to the name of the property you want to set for the retrieved data. In this context, set <code>msg.topic</code> to <code>'cycle-time'</code>, which will be the key in the object that we will create.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.avif 1901w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.webp 1901w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Setting the msg.topic with the Change node to retrieve data from the OPC UA server." loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-setting-nodeid-datatype-QMq46stO3P-1901.jpeg" width="1901" height="816" /></picture>
<em>Setting the msg.topic with the change node to retrieve data from the OPC UA server.</em></p>
<ol start="3">
<li>Drag the <strong>join</strong> node onto the canvas. Set the mode to manual, with the option to create <code>msg.payload</code> using the values of <code>msg.topic</code> as keys. Set the count to 3 and ensure that the interval for all of the <strong>inject</strong> nodes triggering data retrieval is the same. This ensures that the data is collected and aggregated correctly at the same time.</li>
<li>Connect the output of the <strong>OpcUa-Client</strong> node (which retrieves the data) to the input of the <strong>change</strong> node. For example, if I have set the <strong>change</strong> node for the 'cycle-time' data property, connect it to the <strong>OpcUa-Client</strong> node that retrieves this data.</li>
<li>Connect the output of the <strong>change</strong> node to the input of the <strong>join</strong> node.</li>
<li>Repeat this process for all of your data properties.</li>
</ol>
<h3 id="sending-data-to-the-mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#sending-data-to-the-mqtt-broker"></a> Sending Data to the MQTT Broker</h3>
<p>Now, in this section, we will show you how to send the collected data to an MQTT broker:</p>
<ol>
<li>Drag the <strong>mqtt out</strong> node onto the canvas.</li>
<li>Double-click on it and configure it with your MQTT broker details.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt-out-node-config-R0SjwRnNVK-508.avif 508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt-out-node-config-R0SjwRnNVK-508.webp 508w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Configuring the mqtt out node with broker information" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-out-node-config-R0SjwRnNVK-508.jpeg" width="508" height="564" /></picture>
<em>Configuring the mqtt out node with broker information</em></p>
<ol start="3">
<li>Set the topic for your data in the <strong>mqtt out</strong> node.</li>
<li>Connect the output of the <strong>join</strong> node to the input of the <strong>mqtt out</strong> node.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/opcua-to-mqtt-scrt588ft8-1904.gif 1904w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the successful bridging of OPC UA data to MQTT" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-to-mqtt-scrt588ft8-1904.webp" width="1904" height="956" /></picture>
<em>Image showing the successful bridging of OPC UA data to MQTT</em></p>
<div id="nr-flow-144" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow144 = "\n[{\"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\"],[]]},{\"id\":\"1aa02b27b99dfe9d\",\"type\":\"mqtt out\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"topic\":\"/manufacturing/cnc\",\"qos\":\"2\",\"retain\":\"true\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"abd4e6202945fee3\",\"x\":1390,\"y\":380,\"wires\":[]},{\"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\"],[]]},{\"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\"],[]]},{\"id\":\"f5fd1ffafdfe790f\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"Set the topic for the data\",\"rules\":[{\"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\":[{\"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\":[{\"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\":\"/manufacturing/cnc\",\"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\":[]},{\"id\":\"55f43460af9601ec\",\"type\":\"comment\",\"z\":\"807758ec576fbfd8\",\"name\":\"Retrieving the data from mqtt\",\"info\":\"\",\"x\":320,\"y\":600,\"wires\":[]},{\"id\":\"628ab54495901021\",\"type\":\"comment\",\"z\":\"807758ec576fbfd8\",\"name\":\"Bridging OPC UA data to MQTT\",\"info\":\"\",\"x\":350,\"y\":240,\"wires\":[]},{\"id\":\"ce6b8a0e2c8a895d\",\"type\":\"change\",\"z\":\"807758ec576fbfd8\",\"name\":\"\",\"rules\":[{\"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\":[{\"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\":[{\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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://Roni:53530/OPCUA/SimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"abd4e6202945fee3\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"http://broker.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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow144.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-144') })</script>
<h2 id="bridging-mqtt-data-to-opc-ua" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#bridging-mqtt-data-to-opc-ua"></a> Bridging MQTT Data to OPC UA</h2>
<p>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.</p>
<p>For example, an MES can send commands or configuration changes via MQTT, which then need to be applied to an OPC UA-controlled machine.</p>
<ol>
<li>Drag an <strong>mqtt in</strong> node onto the Node-RED canvas and configure it with your MQTT broker details and the appropriate topic where the MES publishes commands.</li>
<li>Drag the <strong>change</strong> node onto the canvas, Set the <code>msg.topic</code> to the node ID and datatype of the property you wish to update.</li>
<li>Add an <strong>OpcUa-Client</strong> node to the canvas and configure it with your OPC UA server. Set the action type to "WRITE" to send the received data.</li>
<li>Connect the output of the <strong>mqtt in</strong> node to the input of the <strong>change</strong> node, and the output of the <strong>change</strong> node to the input of the <strong>OpcUa-Client</strong> node.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/mqtt-to-opcua-6kq1vfLEN3-1910.gif 1910w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Image showing the successful bridging of MQTT data to OPC UA" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt-to-opcua-6kq1vfLEN3-1910.webp" width="1910" height="820" /></picture>
<em>Image showing the successful bridging of OPC UA data to MQTT</em></p>
<div id="nr-flow-145" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow145 = "\n[{\"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\":[[],[]]},{\"id\":\"628ab54495901021\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Bridging MQTT to OPC UA\",\"info\":\"\",\"x\":510,\"y\":140,\"wires\":[]},{\"id\":\"ce6b8a0e2c8a895d\",\"type\":\"change\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"rules\":[{\"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/cnc/\",\"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/cnc/\",\"qos\":\"1\",\"retain\":\"\",\"respTopic\":\"\",\"contentType\":\"\",\"userProps\":\"\",\"correl\":\"\",\"expiry\":\"\",\"broker\":\"abd4e6202945fee3\",\"x\":660,\"y\":400,\"wires\":[]},{\"id\":\"8e339e511c573905\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"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\":[]},{\"id\":\"9dd56eda04f5c5b5\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://Roni:53530/OPCUA/SimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"abd4e6202945fee3\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"http://broker.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\":\"\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow145.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-145') })</script>
<h3 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/opc-ua-to-mqtt-with-node-red/#up-next"></a> Up Next</h3>
<ul>
<li>
<p><a href="https://flowfuse.com/node-red/protocol/mqtt/">Using MQTT with Node-RED</a>
Learn how to integrate MQTT with Node-RED to enhance your IoT solutions with real-time data messaging.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/">How to Build an OPC UA Client Dashboard in Node-RED</a>
Follow a step-by-step guide to create a comprehensive OPC UA client dashboard in Node-RED for effective monitoring and control.</p>
</li>
<li>
<p><a href="https://flowfuse.com/node-red/protocol/opc-ua/">Building a Secure OPC UA Server in Node-RED</a>
Explore best practices for configuring a secure OPC UA server in Node-RED to ensure safe and reliable data exchange.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/">How to Deploy a Basic OPC UA Server in Node-RED</a>
Learn how to quickly deploy a basic OPC UA server in Node-RED for testing and development purposes.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/">Node-RED as a No-Code EtherNet/IP to S7 Protocol Converter</a>
Discover how to use Node-RED to seamlessly convert EtherNet/IP to S7 protocols with Node-RED.</p>
</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/Customise theming in your FlowFuse DashboardCustomising Headers, Themes, and Layouts in FlowFuse Dashboard2024-08-07T00:00:00ZSumit Shinde<p>A recent release of FlowFuse Dashboard (Dashboard 2.0) has taken customization to the next level.</p>
<!--more-->
<p>Previously, users enjoyed the flexibility of tweaking navigation sidebars, themes, and group and page padding. With the new update, you can fully personalize the header too, adding unique elements to enhance your dashboard experience and customize your application to your own branding.</p>
<p>In this article, we'll delve into these exciting new features, including theme adjustments, custom styling, and layout modifications, that empower you to tailor your Node-RED Dashboard like never before.</p>
<h2 id="adding-elements-in-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#adding-elements-in-the-header"></a> Adding Elements in the Header</h2>
<p>To add elements to the header, we can use <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#teleports">Teleports</a> within the <code>ui-template</code> node. This allows elements to be seamlessly rendered in specific areas of the dashboard. This method simplifies the process compared to manually positioning items with CSS, which can be complex, time-consuming, and potentially disruptive to other dashboard elements.</p>
<h3 id="left-side-of-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#left-side-of-the-header"></a> Left Side of the Header</h3>
<p>To render content on the left side of the header, we can teleport content into the <code>#app-bar-title</code> element, where our page name is displayed.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/left-side-area-TYITRmocHF-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/left-side-area-TYITRmocHF-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image of Dashboard showing the #app-bar-title container" alt=""Screenshot of Dashboard showing the #app-bar-title container"" loading="lazy" decoding="async" src="https://flowfuse.com/img/left-side-area-TYITRmocHF-1920.jpeg" width="1920" height="822" /></picture>
<em>Image of Dashboard showing the #app-bar-title container</em></p>
<h4 id="hiding-the-page-name-in-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#hiding-the-page-name-in-the-header"></a> Hiding the Page Name in the Header</h4>
<p>Before proceeding, you should hide the page name on the left side of the header by default. This will ensure that when you add elements to the header, they do not clash with the page name.</p>
<p>To hide the page name:</p>
<ol>
<li>Go to the FlowFuse Dashboard sidebar</li>
<li>Click on to the "Edit settings" option located at the top of the FlowFuse Dashboard sidebar.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'edit setting' option in the dashboard sidebar" alt=""Screenshot showing the 'edit setting' option in the dashboard sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.jpeg" width="1851" height="797" /></picture>
<em>Screenshot showing the 'edit setting' option in the dashboard sidebar</em></p>
<ol start="3">
<li>Untick the option "Show page name in the header bar".
<picture><source type="image/avif" srcset="https://flowfuse.com/img/settings-DvYPDP5sFy-610.avif 610w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/settings-DvYPDP5sFy-610.webp 610w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Show page name in the header bar' option in the dashboard settings" alt=""Screenshot showing the 'Show page name in the header bar' option in the dashboard settings"" loading="lazy" decoding="async" src="https://flowfuse.com/img/settings-DvYPDP5sFy-610.jpeg" width="610" height="765" /></picture>
<em>Screenshot showing the 'Show page name in the header bar' option in the dashboard settings</em></li>
</ol>
<h4 id="example%3A-adding-buttons" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#example%3A-adding-buttons"></a> Example: Adding Buttons</h4>
<ol>
<li>Drag a <code>ui-template</code> node onto the Node-RED Editor canvas.</li>
<li>Double-click on it and select the scope to either <code>ui-scope</code> or <code>page-scope</code>. Selecting <code>ui-scope</code> will render this content on <em>all</em> pages. <code>page-scope</code> will just render to a specified page.</li>
<li>Choose the page on which you want to render the buttons if you selected <code>page-scope</code>, or choose correct ui if <code>ui-scope</code> is selected.</li>
<li>Paste the following Vue snippet into the template widget. In this snippet, note how we specify the "to" attribute targeting the <code>#app-bar-title</code> ID in the teleport tag:</li>
</ol>
<div style="position: relative" id="code-container-81">
<pre class="language-html"><code id="code-81" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Teleport the button to the #app-bar-actions area when mounted --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Teleport</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mounted<span class="token punctuation">"</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#app-bar-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-btn</span><span class="token punctuation">></span></span>Button 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-btn</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-btn</span><span class="token punctuation">></span></span>Button 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-btn</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-btn</span><span class="token punctuation">></span></span>Button 3<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-btn</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Teleport</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">mounted</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Set mounted to true when the component is mounted</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>mounted <span class="token operator">=</span> <span class="token boolean">true</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-81" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Next, you can customize further by adding more buttons or different elements inside the <code><Teleport></code> element.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/button-added-dashboard-j6weolUwP3-1814.avif 1814w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/button-added-dashboard-j6weolUwP3-1814.webp 1814w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of Dashboard showing the added buttons in the header" alt=""Screenshot of Dashboard showing the added buttons in the header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/button-added-dashboard-j6weolUwP3-1814.jpeg" width="1814" height="820" /></picture>
<em>Screenshot of Dashboard showing the added buttons in the header</em></p>
<h4 id="example%3A-adding-logo" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#example%3A-adding-logo"></a> Example: Adding Logo</h4>
<p>If you want to add your brand's logo, you can replace the element inside <teleport> with an <img /> tag. You can do this in the same ui-template widget or in a different ui-template widget:</teleport></p>
<ol>
<li>Drag the <code>ui-template</code> node onto the canvas.</li>
<li>Select the correct scope for that widget to render.</li>
<li>Select the correct page or UI in which you want to render the element.</li>
<li>Paste the same Vue snippet given in the above section into the <code>ui-template</code> widget and replace the code inside <teleport> with the following element:</teleport></li>
</ol>
<div style="position: relative" id="code-container-120">
<pre class="language-html"><code id="code-120" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>32px<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://app.flowfuse.com/ff-logo--wordmark-caps--dark.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>img</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-120" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>You can replace the URL with your logo's URL or set it using the <code>msg.payload</code> as shown in examples given <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#page-name-app-bar-title">documentation</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/logo-added-dashboard-XgvKNzGAIH-1808.avif 1808w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/logo-added-dashboard-XgvKNzGAIH-1808.webp 1808w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Dashboard displaying the added logo in the header" alt=""Screenshot of the Dashboard displaying the added logo in the header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/logo-added-dashboard-XgvKNzGAIH-1808.jpeg" width="1808" height="816" /></picture>
<em>Screenshot of the Dashboard displaying the added logo in the header</em></p>
<h3 id="right-side-of-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#right-side-of-the-header"></a> Right Side of the Header</h3>
<p>To render elements on the right side of the header, you can use the empty div element having the <code>#app-bar-actions</code> ID, in which we can add elements.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/right-side-area-PQiEQskCOM-1895.avif 1895w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/right-side-area-PQiEQskCOM-1895.webp 1895w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of Dashboard showing the #app-bar-actions container" alt=""Screenshot of Dashboard showing the #app-bar-actions container"" loading="lazy" decoding="async" src="https://flowfuse.com/img/right-side-area-PQiEQskCOM-1895.jpeg" width="1895" height="826" /></picture>
<em>Screenshot of Dashboard showing the #app-bar-actions container</em></p>
<h4 id="example%3A-adding-logged-in-user-profile" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#example%3A-adding-logged-in-user-profile"></a> Example: Adding logged in user profile</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/user-profile-9pSbTVp8DC-1814.avif 1814w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/user-profile-9pSbTVp8DC-1814.webp 1814w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of Dashboard displaying the logged in user profile at the right side of header" alt=""Screenshot of Dashboard displaying the logged in user profile at the right side of header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/user-profile-9pSbTVp8DC-1814.jpeg" width="1814" height="826" /></picture>
<em>Screenshot of Dashboard displaying the logged in user profile at the right side of header</em></p>
<p>In this section, we will add the user profile of the currently logged-in user to the right side of the header. Make sure you have installed "@flowfuse/node-red-dashboard-2-user-addon" via the palette manager and enabled <a href="https://flowfuse.com/docs/user/instance-settings/#flowfuse-user-authentication">FlowFuse User Authentication</a>. Each message emitted by the FlowFuse Dashboard widget will include the logged-in user information under <code>msg._client.user</code>. Additionally the <a href="https://dashboard.flowfuse.com/contributing/guides/state-management.html#setup-store">setup object</a> will also contain this information under <code>setup.socketio.auth.user</code>.</p>
<ol>
<li>Drag the ui-template widget onto the canvas.</li>
<li>Select the correct scope for that widget to render.</li>
<li>Select the correct page or UI in which you want to render the element.</li>
<li>Paste the same Vue snippet given below into the <code>ui-template</code> widget:</li>
</ol>
<div style="position: relative" id="code-container-167">
<pre class="language-html"><code id="code-167" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Teleporting user info to #app-bar-actions, which is the ID of the action bars' right corners area --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Teleport</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>loaded<span class="token punctuation">"</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#app-bar-actions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>user-info<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Displaying user image --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">:src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>setup.socketio.auth.user.image<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /> <span class="token comment"><!-- Greeting the user --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Hi, <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Teleport</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Flag to indicate if the component is loaded</span><br /> <span class="token literal-property property">loaded</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// This function is called when the component is inserted into the DOM.</span><br /> <span class="token comment">// Setting loaded to true here ensures the component is ready to access #app-bar-actions,</span><br /> <span class="token comment">// as it's now part of the same DOM structure.</span><br /> <span class="token comment">// Accessing it before mounted() would cause an error because the component wouldn't be initialized in the DOM yet.</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>loaded <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment">// Setting loaded to true to indicate that the component has been mounted successfully</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="token comment">/* Styling for user info display */</span><br /><span class="token selector">.user-info</span> <span class="token punctuation">{</span><br /> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br /> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br /> <span class="token property">gap</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token comment">/* Styling for user avatar image*/</span><br /><span class="token selector">.user-info img</span> <span class="token punctuation">{</span><br /> <span class="token property">width</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span><br /> <span class="token property">height</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-167" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>For detailed guide on this section, refer to the guide on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/">Displaying logged in user on FlowFuse Dashboard</a>. Furthermore, if you want to add logos or buttons on the right side similar to the left side of the header, you just need to replace the to attribute with the <code>#app-bar-actions</code>.</p>
<h3 id="centering-header-items" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#centering-header-items"></a> Centering Header Items</h3>
<p>Sometimes you may want to center or position items added to either the <code>#app-bar-title</code> or the <code>#app-bar-actions</code>. By default, these elements do not have a specified width, and when you add items into them, they grow to fit their content. To center the elements, you first need to ensure that they are sized appropriately.</p>
<h4 id="centering-items-in-the-left-side-of-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#centering-items-in-the-left-side-of-the-header"></a> Centering Items in the Left Side of the Header</h4>
<p>To center items added to the <code>#app-bar-title</code>, apply the following CSS in the <code><style></code> tag of the <code>ui-template</code> widget:</p>
<div style="position: relative" id="code-container-183">
<pre class="language-css"><code id="code-183" class="language-css"><span class="token selector">#app-bar-title</span> <span class="token punctuation">{</span><br /> <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br /> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-183" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="centering-items-in-the-right-side-of-the-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#centering-items-in-the-right-side-of-the-header"></a> Centering Items in the Right Side of the Header</h4>
<p>To center items in the <code>#app-bar-actions</code> area, add the following CSS to the <code><style></code> tag of the <code>ui-template</code> widget:</p>
<div style="position: relative" id="code-container-190">
<pre class="language-css"><code id="code-190" class="language-css"><span class="token selector">#v-toolbar__append</span> <span class="token punctuation">{</span><br /> <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-190" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="styling-header" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#styling-header"></a> Styling Header</h2>
<p>One of the significant customization features we've added recently is the ability to style the header in different ways.</p>
<p>To style the header:</p>
<ol>
<li>Go to the FlowFuse Dashboard sidebar</li>
<li>Click on to the "Edit settings" option located at the top of the FlowFuse Dashboard sidebar.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'edit setting' option in the dashboard sidebar" alt=""Screenshot showing the 'edit setting' option in the dashboard sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-setting-button--iC5SnEpLI-1851.jpeg" width="1851" height="797" /></picture>
<em>Screenshot showing the 'edit setting' option in the dashboard sidebar</em></p>
<ol start="3">
<li>Select the desired option from the "Header Options" dropdown.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/header-style-options-ARwB84qNIt-607.avif 607w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/header-style-options-ARwB84qNIt-607.webp 607w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the header style options in the dashboard settings" alt=""Screenshot showing the header style options in the dashboard settings"" loading="lazy" decoding="async" src="https://flowfuse.com/img/header-style-options-ARwB84qNIt-607.jpeg" width="607" height="759" /></picture>
<em>Screenshot showing the header style options in the dashboard settings</em></p>
<p>The following options are available for header styling:</p>
<h3 id="default" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#default"></a> Default</h3>
<p>This option as it name suggest it is the default option set for header. In which the header will get hidden if we scrolled down.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/default-header-uu8FX69LSX-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the dashboard with default header" alt=""Image showing the dashboard with default header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/default-header-uu8FX69LSX-800.webp" width="800" height="450" /></picture>
<em>Image showing the dashboard with default header</em></p>
<h3 id="hidden" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#hidden"></a> Hidden</h3>
<p>Selecting this option completely hides the header, allowing you to use that space for other purposes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/hidden-header-SzIREU0W3f-1811.avif 1811w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/hidden-header-SzIREU0W3f-1811.webp 1811w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the dashboard with hidden header" alt=""Image showing the dashboard with hidden header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/hidden-header-SzIREU0W3f-1811.jpeg" width="1811" height="819" /></picture>
<em>Image showing the dashboard with hidden header</em></p>
<h3 id="fixed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#fixed"></a> Fixed</h3>
<p>Selecting this option keeps the header fixed at the top. This means that when you scroll the page down, the header will remain visible.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/fixed-header-9hmyMb_US9-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the dashboard with fixed header" alt=""Image showing the dashboard with fixed header"" loading="lazy" decoding="async" src="https://flowfuse.com/img/fixed-header-9hmyMb_US9-800.webp" width="800" height="450" /></picture>
<em>Image showing the dashboard with fixed header</em></p>
<h2 id="changing-dashboard-theme" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#changing-dashboard-theme"></a> Changing Dashboard Theme</h2>
<p>In this section of the guide, you will learn how to change the Dashboard theme, where you can adjust the colors of the header, navigation sidebar, group and page backgrounds, the border color of groups, and the padding, sizing, and gaps between pages, groups, and widgets.</p>
<p>To edit the existing theme:</p>
<ol>
<li>Go to the FlowFuse Dashboard sidebar.</li>
<li>Switch to the "Theme" tab.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-theme-tab-UzDAoNwYyb-1836.avif 1836w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-theme-tab-UzDAoNwYyb-1836.webp 1836w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the dashboard theme tab in the sidebar" alt=""Screenshot showing the dashboard theme tab in the sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-theme-tab-UzDAoNwYyb-1836.jpeg" width="1836" height="814" /></picture>
<em>Screenshot showing the dashboard theme tab in the sidebar</em></p>
<ol start="3">
<li>Click on the edit button next to the theme.</li>
<li>You can adjust the header color and the primary color (which applies to the navigation sidebar and elements like buttons and dropdowns) under the "Primary" section. In the "Pages" section, set the background color for pages, and in the "Groups" section, adjust the background color and border color of groups.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-theme-settings-vuvXjbk1C3-509.avif 509w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-theme-settings-vuvXjbk1C3-509.webp 509w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the theme properties dialog" alt=""Screenshot showing the theme properties dialog"" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-theme-settings-vuvXjbk1C3-509.jpeg" width="509" height="782" /></picture>
<em>Screenshot showing the theme properties dialog</em></p>
<ol start="5">
<li>Under "Sizing," adjust the page padding (the space between dashboard groups), the page border, group gap, group border radius (the thickness of the group border), and widget gap.</li>
</ol>
<p>For more information on theme, how to add new themes, and set themes for pages, refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#understanding-dashboard-2.0-theme">Comprehensive guide: FlowFuse Dashboard layout, sidebar, and styling</a>. Additionally, this guide covers FlowFuse Dashboard layouts, themes, and custom styling in detail.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/customise-theming-in-your-dashboards/#conclusion"></a> Conclusion</h2>
<p>In this article, we explored FlowFuse Dashboard's new customization features. We focused on adding elements like buttons and logos to the header, and discussed styling options such as default, hidden, and fixed for headers. We also covered how to adjust dashboard themes to personalize colors and layout. These insights empower users to create more personalized and functional Node-RED dashboards.</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Customise%20theming%20in%20your%20FlowFuse%20Dashboard">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/FlowFuse Dashboard vs UI-Builder: A Comprehensive ComparisonUnderstanding the Differences Between FlowFuse Dashboard and UI-Builder2024-08-06T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="how-easy-is-it-to-install%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-easy-is-it-to-install%3F"></a> How Easy is it to Install?</h2>
<h3 id="flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard"></a> FlowFuse Dashboard</h3>
<p>When searching for <code>flowfuse/node-red-dashboard</code> 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.</p>
<h3 id="ui-builder" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder"></a> UI-Builder</h3>
<p>Finding UI-Builder is straightforward, as its package name <code>node-red-contrib-uibuilder</code> 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.</p>
<h2 id="how-easy-is-it-to-get-started%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-easy-is-it-to-get-started%3F"></a> How Easy is it to Get Started?</h2>
<h3 id="flowfuse-dashboard-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-1"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<h3 id="ui-builder-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-1"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="migrating-from-node-red-dashboard-(dashboard-1.0)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#migrating-from-node-red-dashboard-(dashboard-1.0)"></a> Migrating from Node-RED Dashboard (Dashboard 1.0)</h2>
<p><a href="https://flows.nodered.org/node/node-red-dashboard">Node-RED Dashboard</a> 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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/">Node-RED Dashboard Formally Deprecated</a>.</p>
<h3 id="flowfuse-dashboard-2" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-2"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<p>To facilitate the transition, FlowFuse, the creator of FlowFuse Dashboard, provides a <a href="https://flowfuse.com/product/dashboard/#migration-service">migration service</a> 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.</p>
<h3 id="ui-builder-2" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-2"></a> UI-Builder</h3>
<p>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.</p>
<p>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.</p>
<h2 id="how-active-is-the-project's-development%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-active-is-the-project's-development%3F"></a> How Active is the Project's Development?</h2>
<h3 id="flowfuse-dashboard-3" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-3"></a> FlowFuse Dashboard</h3>
<p><a href="https://github.com/FlowFuse/node-red-dashboard/graphs/contributors">FlowFuse Dashboard</a>, 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-2-commits-LdUhW6WXAo-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-2-commits-LdUhW6WXAo-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of the FlowFuse Dashboard GitHub commit chart" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-2-commits-LdUhW6WXAo-650.jpeg" width="650" height="167" /></picture><br />
<em>Screenshot of the FlowFuse Dashboard GitHub commit chart.</em></p>
<h3 id="ui-builder-3" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-3"></a> UI-Builder</h3>
<p><a href="https://github.com/TotallyInformation/node-red-contrib-uibuilder/graphs/contributors">UI-Builder</a> 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-builder-commits-NL6M1LpSB1-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-builder-commits-NL6M1LpSB1-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of the UI-Builder GitHub commit chart" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-builder-commits-NL6M1LpSB1-650.jpeg" width="650" height="165" /></picture><br />
<em>Screenshot of the UI-Builder GitHub commit chart.</em></p>
<h2 id="pre-built-ui-elements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#pre-built-ui-elements"></a> Pre-Built UI Elements</h2>
<h3 id="flowfuse-dashboard-4" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-4"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<h3 id="ui-builder-4" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-4"></a> UI-Builder</h3>
<p>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/CSS. 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.</p>
<h2 id="changing-the-ui-at-runtime" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#changing-the-ui-at-runtime"></a> Changing the UI at Runtime</h2>
<h3 id="flowfuse-dashboard-5" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-5"></a> FlowFuse Dashboard</h3>
<p>FlowFuse Dashboard supports dynamic control of the UI via the msg object. Each UI widget supports the <code>msg.ui_update</code> 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.</p>
<h3 id="ui-builder-5" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-5"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="data-visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#data-visualization"></a> Data Visualization</h2>
<h3 id="flowfuse-dashboard-6" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-6"></a> FlowFuse Dashboard</h3>
<p>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 <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html">chart</a> and <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-gauge.html">gauge widgets</a>. The chart widgets support various types, including line, bar, scatter, pie, and donut charts, while the gauge widgets offer options like tile, 3/4 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.</p>
<h3 id="ui-builder-6" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-6"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="building-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#building-layout"></a> Building Layout</h2>
<h3 id="flowfuse-dashboard-7" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-7"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<p>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.</p>
<h3 id="ui-builder-7" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-7"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="responsiveness-on-mobile" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#responsiveness-on-mobile"></a> Responsiveness on Mobile</h2>
<h3 id="flowfuse-dashboard-8" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-8"></a> FlowFuse Dashboard</h3>
<p>FlowFuse Dashboard is designed with <a href="https://dashboard.flowfuse.com/layouts/">responsiveness</a> 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.</p>
<h3 id="ui-builder-8" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-8"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="how-much-customization-is-available%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-much-customization-is-available%3F"></a> How Much Customization is Available?</h2>
<h3 id="flowfuse-dashboard-9" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-9"></a> FlowFuse Dashboard</h3>
<p>FlowFuse Dashboard, built on Vue.js, offers a range of customization options through its widget configurations and settings.</p>
<p>While it provides predefined UI elements, users can override CSS and theming using the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/">ui_template</a> widget to enhance the dashboard's appearance.</p>
<p>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.</p>
<h3 id="ui-builder-9" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-9"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="can-it-be-installed-on-mobile%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#can-it-be-installed-on-mobile%3F"></a> Can it be Installed on Mobile?</h2>
<h3 id="flowfuse-dashboard-10" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-10"></a> FlowFuse Dashboard</h3>
<p>FlowFuse Dashboard is built and deployed as a Progressive Web App. This means whilst it's built as a web application, it can be <a href="https://dashboard.flowfuse.com/user/pwa.html#installing-dashboards-on-mobile">installed on your mobile device</a> and run as a standalone application, behaving as if it was a native app.</p>
<h3 id="ui-builder-10" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-10"></a> Ui-Builder</h3>
<p>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.</p>
<h2 id="how-about-performance-and-speed%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-about-performance-and-speed%3F"></a> How About Performance and Speed?</h2>
<h3 id="flowfuse-dashboard-11" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-11"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<h3 id="ui-builder-11" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-11"></a> UI-Builder</h3>
<p>UI-Builder provides faster initial load times and smooth performance across customizations, ensuring high-speed interactions.</p>
<h2 id="how-is-the-overall-user-experience%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-is-the-overall-user-experience%3F"></a> How is the Overall User Experience?</h2>
<h3 id="flowfuse-dashboard-12" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-12"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<h3 id="ui-builder-12" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-12"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="how-active-is-the-user-community%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-active-is-the-user-community%3F"></a> How Active is the User Community?</h2>
<h3 id="flowfuse-dashboard-13" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-13"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-2-download-dRYV7GBZ7l-538.avif 538w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-2-download-dRYV7GBZ7l-538.webp 538w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt=""Screenshot of the FlowFuse Dashboard package's weekly download chart from npm"" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-2-download-dRYV7GBZ7l-538.jpeg" width="538" height="111" /></picture>
<em>Screenshot of the FlowFuse Dashboard package's weekly download chart from npm</em></p>
<h3 id="ui-builder-13" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-13"></a> UI-Builder</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-builder-downloads-uUBcR1aggk-540.avif 540w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-builder-downloads-uUBcR1aggk-540.webp 540w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt=""Screenshot of the Ui-Builder package's weekly download chart from npm"" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-builder-downloads-uUBcR1aggk-540.jpeg" width="540" height="109" /></picture>
<em>Screenshot of the Ui-Builder package's weekly download chart from npm</em></p>
<h2 id="how-good-is-the-support-and-documentation%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#how-good-is-the-support-and-documentation%3F"></a> How Good is the Support and Documentation?</h2>
<h3 id="flowfuse-dashboard-14" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-14"></a> FlowFuse Dashboard</h3>
<p>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.</p>
<h3 id="ui-builder-14" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-14"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="what-are-the-future-development-plans%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#what-are-the-future-development-plans%3F"></a> What are the future development plans?</h2>
<h3 id="flowfuse-dashboard-15" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#flowfuse-dashboard-15"></a> FlowFuse Dashboard</h3>
<p>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 <a href="https://github.com/orgs/FlowFuse/projects/15/views/4">FlowFuse Dashboard GitHub project board</a>, where ongoing and upcoming features are documented.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-650.avif 650w, https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-650.webp 650w, https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-650.jpeg 650w, https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt=""Screenshot of the FlowFuse Dashboard GitHub project board"" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-2-project-plan-jwtgKqOq_Q-650.jpeg" width="1300" height="548" /></picture>
<em>Screenshot of the FlowFuse Dashboard GitHub project board</em></p>
<p>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.</p>
<h3 id="ui-builder-15" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#ui-builder-15"></a> UI-Builder</h3>
<p>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.</p>
<h2 id="summary-table" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#summary-table"></a> Summary Table</h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>FlowFuse Dashboard</th>
<th>UI-Builder</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Ease of Installation</strong></td>
<td><strong>Moderately easy</strong>: Search for <code>flowfuse/node-red-dashboard</code> can be confusing due to multiple nodes; useful documentation available</td>
<td><strong>Easy</strong>: Clear package name <code>node-red-ui-builder</code> makes it straightforward to find</td>
</tr>
<tr>
<td><strong>Ease of Getting Started</strong></td>
<td><strong>Easy</strong>: Low-code interface with a sidebar for managing UI elements; intuitive for users of varying technical skills</td>
<td><strong>Challenging</strong>: Requires understanding of coding and UI design; lacks a low-code approach</td>
</tr>
<tr>
<td><strong>Migration from Node-RED Dashboard</strong></td>
<td><strong>Smooth</strong>: Migration service provided to transition from Node-RED Dashboard 1 to FlowFuse Dashboard</td>
<td><strong>Complex</strong>: Requires rebuilding dashboards from scratch; significant effort needed for migration</td>
</tr>
<tr>
<td><strong>Development Activity</strong></td>
<td><strong>High</strong>: Regular updates and improvements; actively maintained by a dedicated team</td>
<td><strong>Moderate</strong>: Active but shows a decline in updates; less frequent enhancements</td>
</tr>
<tr>
<td><strong>UI Elements Collection</strong></td>
<td><strong>Extensive</strong>: Includes a wide range of widgets like forms, charts, and gauges; built on Vue.js with Vuetify support</td>
<td><strong>Flexible but complex</strong>: Allows any HTML element; requires custom coding and handling of UI elements</td>
</tr>
<tr>
<td><strong>Visualization</strong></td>
<td><strong>Easy</strong>: Built-in charting options for various types; additional options with third-party libraries</td>
<td><strong>Complex</strong>: No built-in charts; requires integration of third-party libraries for visualization</td>
</tr>
<tr>
<td><strong>Dynamic UI Control</strong></td>
<td><strong>Supported</strong>: Can dynamically control and update UI components using <code>msg.ui_update</code> property</td>
<td><strong>Supported</strong>: Dynamic updates possible, but can be more complex to implement</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>Web Layout Support</strong></td>
<td><strong>Predefined</strong>: Offers three main layouts with some options for custom styling</td>
<td><strong>Customizable</strong>: No predefined layouts; users must create their own using CSS</td>
</tr>
<tr>
<td><strong>Device and Screen Size Support</strong></td>
<td><strong>Excellent</strong>: Responsive design adapts well to various devices and screen sizes</td>
<td><strong>Variable</strong>: Responsiveness depends on user's implementation and knowledge of CSS</td>
</tr>
<tr>
<td><strong>Customization</strong></td>
<td><strong>Good</strong>: Offers customization through widget settings and custom CSS and ui-template; supports Vue components and third-party libraries</td>
<td><strong>High</strong>: Full control over UI design and layout with custom coding; requires expertise in frontend development</td>
</tr>
<tr>
<td><strong>App Installation</strong></td>
<td><strong>Available</strong>: Can be installed as a Progressive Web App (PWA) for use as a standalone application</td>
<td><strong>Not available</strong>: Designed as a web-based tool; cannot be installed as an app</td>
</tr>
<tr>
<td><strong>Performance and Speed</strong></td>
<td><strong>Reliable</strong>: Good performance with consistent updates; may have slower initial load times on mobile</td>
<td><strong>Good</strong>: Generally good performance on both desktop and mobile</td>
</tr>
<tr>
<td><strong>Support and Documentation</strong></td>
<td><strong>Robust</strong>: Comprehensive, regularly updated documentation and active community support</td>
<td><strong>Good</strong>: Detailed but may be complex; documentation includes technical jargon</td>
</tr>
<tr>
<td><strong>Overall User Experience</strong></td>
<td><strong>User-friendly</strong>: Accessible and easy to use with low-code approach; suitable for a wide range of users</td>
<td><strong>Flexible but complex</strong>: Offers greater customization but requires coding expertise; can be daunting for non-developers</td>
</tr>
<tr>
<td><strong>User Community Activity</strong></td>
<td><strong>Large and active</strong>: High engagement in forums and discussions; growing number of downloads</td>
<td><strong>Smaller but dedicated</strong>: Active users, but less engagement compared to FlowFuse Dashboard</td>
</tr>
<tr>
<td><strong>Future Development Plans</strong></td>
<td><strong>Active</strong>: Continuous enhancements and new features planned; updates tracked on GitHub project board</td>
<td><strong>Less transparent</strong>: No detailed roadmap; future features are less clearly communicated</td>
</tr>
</tbody>
</table>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/comparing-dashboard-2-with-uibuilder/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<p>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.</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=FlowFuse%20Dashboard%20vs%20UI-Builder%3A%20A%20Comprehensive%20Comparison">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/FlowFuse 2.7: Improved management at scale & AI JSON EditorWe've extended the AI-infused Node-RED experience to the JSON Editor, plus improvements to managing and searching your resources on FlowFuse.2024-08-01T00:00:00ZJoe Pavitt<p>FlowFuse 2.7 has had a big focus on user experience improvements, particularly centered around teams running at large scales, with over a thousand devices and instances. With this in mind, we've introduced a new search feature that allows you to search across all of your FlowFuse instances, devices and applications from a single place, made it easier to manage your devices with bulk actions, introduced a new notifications inbox to give you a clearer picture on activity across your team, and just for good measure, we've also extended the AI-infused Node-RED experience to the JSON Editor.</p>
<!--more-->
<h2 id="flowfuse-assistant-in-the-json-editor" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#flowfuse-assistant-in-the-json-editor"></a> FlowFuse Assistant in the JSON Editor</h2>
<p>Last release we saw the introduction of the FlowFuse Assistant, and in this release we're rolling this out further to include Node-RED's JSON Editor.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ask-assistant-json-3Ak05qyfOi-1500.avif 1500w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ask-assistant-json-3Ak05qyfOi-1500.webp 1500w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="(Left) Screenshot of the AI prompt for a list of 4-legged creatures and (Right) the resulting JSON generated by the FlowFuse Assistant" loading="lazy" decoding="async" src="https://flowfuse.com/img/ask-assistant-json-3Ak05qyfOi-1500.jpeg" width="1500" height="591" /></picture>
<em>(Left) Screenshot of the AI prompt for a list of 4-legged creatures and (Right) the resulting JSON generated by the FlowFuse Assistant</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ask-assistant-json-2--ggVDh4TQt-1200.avif 1200w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ask-assistant-json-2--ggVDh4TQt-1200.webp 1200w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="(Left) Screenshot of the AI prompt input for a list of simulated devices and (Right) the resulting JSON generated by the FlowFuse Assistant" loading="lazy" decoding="async" src="https://flowfuse.com/img/ask-assistant-json-2--ggVDh4TQt-1200.jpeg" width="1200" height="836" /></picture>
<em>(Left) Screenshot of the AI prompt input for a list of simulated devices and (Right) the resulting JSON generated by the FlowFuse Assistant</em></p>
<p>This new feature empowers you to create custom data sets within seconds, meaning you can sketch out Dashboards or test Node-RED flows before connecting to real, production environments.</p>
<h2 id="managing-resources-at-scale" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#managing-resources-at-scale"></a> Managing Resources at Scale</h2>
<p>As mentioned in the introduction, a big focus on this release, and moving forward into the next couple too, is around improving the user experience when managing large numbers of devices and instances.</p>
<h3 id="centralized-search" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#centralized-search"></a> Centralized Search</h3>
<p>We have customers with over a thousand devices and instances running in FlowFuse. It can be very difficult to find the resource you're looking for. That's why we've introduced a new search feature that allows you to search across all of your FlowFuse instances, devices and applications from a single place.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/applications-search-gKLJe6n3IG-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/applications-search-gKLJe6n3IG-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Application Search feature in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/applications-search-gKLJe6n3IG-1920.jpeg" width="1920" height="1101" /></picture>
<em>Screenshot showing the new Application Search feature in FlowFuse</em></p>
<p>The search filters as you type, making it easy to find the resource you're looking for. It will keep reference to any applications and or child instances/devices where appropriate too.</p>
<h3 id="notifications-inbox" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#notifications-inbox"></a> Notifications Inbox</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/notifications-inbox-xagIg98Qs_-1420.avif 1420w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/notifications-inbox-xagIg98Qs_-1420.webp 1420w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Notifications Inbox in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/notifications-inbox-xagIg98Qs_-1420.jpeg" width="1420" height="482" /></picture>
<em>Screenshot showing the new Notifications Inbox in FlowFuse</em></p>
<p>This is a first step in introducing richer notifications across FlowFuse. We've introduced a new notifications inbox that will show you all the activity across your team. Currently, this just shows new team invites, but will soon show instance/device activity that needs your attention and more.</p>
<h3 id="bulk-device-actions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#bulk-device-actions"></a> Bulk Device Actions</h3>
<p>We've now enabled the ability to perform bulk actions on devices in FlowFuse. This is particularly useful for managing large numbers of devices, where you might want to update the settings, or delete multiple devices at once.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/bulk-delete-1-3ZnjI7aLBn-808.avif 808w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/bulk-delete-1-3ZnjI7aLBn-808.webp 808w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Bulk Device Actions feature in FlowFuse" loading="lazy" decoding="async" src="https://flowfuse.com/img/bulk-delete-1-3ZnjI7aLBn-808.jpeg" width="808" height="813" /></picture>
<em>Screenshot showing the new Bulk Device Actions feature in FlowFuse</em></p>
<p>For now, we just support bulk delete, but we're planning to add <a href="https://github.com/FlowFuse/flowfuse/issues/2381">more bulk actions</a> in the very near future.</p>
<h3 id="and-much-more..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#and-much-more..."></a> And Much More...</h3>
<p>For a full list of everything that went into our 2.7 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.7.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#try-it-out"></a> Try it out</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install using <a href="https://flowfuse.com/docs/install/docker/">Docker</a> or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud: <a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.7, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/08/flowfuse-2-7-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/How to Set Up SSO LDAP for Node-REDStep-by-step guide on setting up SSO LDAP for your self-hosted FlowFuse platform2024-07-29T00:00:00ZSumit Shinde<p>A few days ago, we published a <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/">blog</a> explaining SSO and how to set up SAML for your self-hosted FlowFuse. Now, in this guide, we will walk you through the process of setting up SSO with LDAP for your self-hosted FlowFuse. We will use OpenLDAP as the provider and cover everything from introducing LDAP, how it works, installing and configuring OpenLDAP, managing users (create, delete, update), and finally setting up FlowFuse for SSO with LDAP.</p>
<!--more-->
<h2 id="understanding-ldap-sso%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#understanding-ldap-sso%3F"></a> Understanding LDAP SSO?</h2>
<h3 id="what-is-ldap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#what-is-ldap"></a> What is LDAP</h3>
<p>LDAP (Lightweight Directory Access Protocol) is a protocol used to access and manage directory information. In the context of network administration, a directory service acts as a specialized database that stores and organizes information about users, devices, and other resources. Think of it as a digital phonebook for your network, allowing centralized management and efficient access to information.</p>
<p>LDAP enables applications and services to query, add, update, and delete directory entries stored on LDAP servers. It simplifies identity management by enabling easy authentication, authorization, and quick access to information across distributed systems.</p>
<h3 id="how-ldap-sso-works" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#how-ldap-sso-works"></a> How LDAP SSO works</h3>
<ol>
<li>
<p><strong>User Authentication Request:</strong> A user attempts to access a service or application that requires authentication.</p>
</li>
<li>
<p><strong>SSO Initiation:</strong> The application forwards the authentication request to the Identity Provider (IdP) configured with LDAP, such as OpenLDAP.</p>
</li>
<li>
<p><strong>LDAP Authentication:</strong> The IdP (LDAP server) verifies the user's credentials against its directory.</p>
</li>
<li>
<p><strong>Authentication Response:</strong> If the credentials are valid, the LDAP server sends an authentication response (usually a token or assertion) back to the application.</p>
</li>
<li>
<p><strong>Access Granted:</strong> The application grants access to the user based on the authentication response received from the LDAP server.</p>
</li>
</ol>
<h2 id="setting-up-sso-ldap-for-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#setting-up-sso-ldap-for-flowfuse"></a> Setting up SSO LDAP for FlowFuse</h2>
<p>Before we proceed, ensure that FlowFuse is deployed on your server with an Enterprise license and you have ssh connection with it so that you can run commands on the server. If you haven't installed it yet, please check out our <a href="https://flowfuse.com/docs/install/introduction/">documentation on installing FlowFuse</a> or our blog on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/">deploying FlowFuse on Ubuntu with Docker</a>.</p>
<h3 id="installing-and-configuring-openldap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#installing-and-configuring-openldap"></a> Installing and Configuring OpenLDAP</h3>
<p>Throughout this section, we will install and configure OpenLDAP on your Ubuntu server. Make sure to replace the commands and configs with your details. If you are using a different distribution, you can follow other resources available on the internet for installation and configuration, as well as managing of users.</p>
<ol>
<li>
<p>Set the hostname for your LDAP server:</p>
<div style="position: relative" id="code-container-63">
<pre class="language-bash"><code id="code-63" class="language-bash">hostnamectl set-hostname ldap.<span class="token operator"><</span>your-domain<span class="token operator">></span>.com</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-63" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Add the server IP to <code>/etc/hosts</code>:</p>
<div style="position: relative" id="code-container-69">
<pre class="language-bash"><code id="code-69" class="language-bash"><span class="token builtin class-name">echo</span> <span class="token string">'<your_server_ip> ldap.<your-domain>.com'</span> <span class="token operator">>></span> /etc/hosts</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-69" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Install OpenLDAP and related utilities:</p>
<div style="position: relative" id="code-container-75">
<pre class="language-bash"><code id="code-75" class="language-bash"><span class="token function">apt</span> <span class="token function">install</span> slapd ldap-utils</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-75" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Set an administrator password for LDAP during installation and confirm it in the next prompt.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to set the administrator password" alt=""Screenshot of prompt asking to set the administrator password while installation"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.jpeg" width="650" height="315" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to conform the administrator password" alt=""Screenshot of prompt asking to conform the administrator password"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.jpeg" width="650" height="322" /></picture></p>
<ol start="5">
<li>
<p>Reconfigure the slapd package:</p>
<div style="position: relative" id="code-container-94">
<pre class="language-bash"><code id="code-94" class="language-bash">dpkg-reconfigure slapd</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-94" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>When asked to omit server configuration, select ‘NO’</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Cofiguring-slapd-ubuntu-_DY8Xzl3pr-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Cofiguring-slapd-ubuntu-_DY8Xzl3pr-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to omit the server configuration" alt=""Screenshot of prompt asking to omit the server configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Cofiguring-slapd-ubuntu-_DY8Xzl3pr-650.jpeg" width="650" height="276" /></picture></p>
<ol start="7">
<li>
<p>Configure the base DN (Distinguished Name) for your LDAP directory:</p>
<ul>
<li>Use your domain name to construct the base DN. For example, if your domain is <code>my-flows.site</code>, the base DN would be <code>dc=my-flows,dc=site</code> and press 'ENTER' to confirm.</li>
</ul>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/prompt-for-domain-lv6UZ7MPUz-533.avif 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/prompt-for-domain-lv6UZ7MPUz-533.webp 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to enter your domain to construct the base DN" alt="Screenshot of prompt asking to enter your domain to construct the base DN"" loading="lazy" decoding="async" src="https://flowfuse.com/img/prompt-for-domain-lv6UZ7MPUz-533.jpeg" width="533" height="296" /></picture></p>
<ul>
<li>Provide a name for your organization, which will also be part of the base DN and press 'Enter.'</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/prompt-for-org-eHjyc8jqR--534.avif 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/prompt-for-org-eHjyc8jqR--534.webp 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to enter your org name" alt=""Screenshot of prompt asking to enter your org name"" loading="lazy" decoding="async" src="https://flowfuse.com/img/prompt-for-org-eHjyc8jqR--534.jpeg" width="534" height="230" /></picture></p>
<ol start="9">
<li>Enter the Administrator password for your LDAP directory.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to set the administrator password" alt=""Screenshot of prompt asking to set the administrator password"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Ldap-Server-Admin-Password-Ubuntu-THNVIGz8FC-650.jpeg" width="650" height="315" /></picture></p>
<ol start="10">
<li>Confirm the password.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of prompt asking to conform the administrator password" alt=""Screenshot of prompt asking to conform the administrator password"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Confirm-Ldap-Admin-Passsword-Ubuntu-417xB2Cwz3-650.jpeg" width="650" height="322" /></picture></p>
<ol start="11">
<li>When asked to remove the database when slapd is purged, select ‘NO’.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Database-Removal-Slapd-Ubuntu-__Im5dGDDf-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Database-Removal-Slapd-Ubuntu-__Im5dGDDf-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the prompt asking to remove the database when slapd is purged" alt=""Screenshot of the prompt asking to remove the database when slapd is purged"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Database-Removal-Slapd-Ubuntu-__Im5dGDDf-650.jpeg" width="650" height="312" /></picture></p>
<ol start="12">
<li>Select ‘Yes’ to remove the old database to create room for a new database.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Select-Yes-Move-Old-Databases-Slapd-Ubuntu-ts7wmO3v5--650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Select-Yes-Move-Old-Databases-Slapd-Ubuntu-ts7wmO3v5--650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the prompt asking to remove the old database" alt=""Screenshot of the prompt asking to remove the old database"" loading="lazy" decoding="async" src="https://flowfuse.com/img/Select-Yes-Move-Old-Databases-Slapd-Ubuntu-ts7wmO3v5--650.jpeg" width="650" height="318" /></picture></p>
<ol start="13">
<li>Edit the main OpenLDAP configuration file:</li>
</ol>
<div style="position: relative" id="code-container-179">
<pre class="language-bash"><code id="code-179" class="language-bash"><span class="token function">sudo</span> <span class="token function">nano</span> /etc/ldap/ldap.conf</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-179" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="14">
<li>Uncomment the lines beginning with “BASE” and “URI”, for example, my domain is <code>my-flows.site</code>, we have updated the file as below, but you have to update it according to your domain:</li>
</ol>
<pre><code>BASE `dc=my-flows,dc=site.`
URI `ldap://ldap.my-flows.site.`
</code></pre>
<h3 id="adding%2C-updating%2C-and-deleting-groups-and-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#adding%2C-updating%2C-and-deleting-groups-and-users"></a> Adding, Updating, and Deleting Groups and Users</h3>
<h4 id="adding-groups-and-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#adding-groups-and-users"></a> Adding Groups and Users</h4>
<ol>
<li>
<p>Create a file for the base groups and open the editor:</p>
<div style="position: relative" id="code-container-199">
<pre class="language-bash"><code id="code-199" class="language-bash"><span class="token function">nano</span> groups.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-199" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Add the following content to <code>groups.ldif</code>, which will create the <code>users</code> group, make sure when you create a new group the gidNumber and ou is unique:</p>
<pre><code>dn: ou=users,dc=my-flows,dc=site
objectClass: organizationalUnit
ou: users
gidNumber: 7000
</code></pre>
</li>
<li>
<p>Add the groups to the LDAP directory:</p>
<div style="position: relative" id="code-container-211">
<pre class="language-bash"><code id="code-211" class="language-bash">ldapadd <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token parameter variable">-f</span> groups.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-211" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Create a password for the user and store the encrypted password:</p>
</li>
</ol>
<div style="position: relative" id="code-container-219">
<pre class="language-bash"><code id="code-219" class="language-bash">Slappasswd <span class="token parameter variable">-g</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-219" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>
<p>Create a file for the user:</p>
<div style="position: relative" id="code-container-225">
<pre class="language-bash"><code id="code-225" class="language-bash"><span class="token function">nano</span> user.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-225" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Add the following content to user.ldif. Replace the placeholders with actual values for <code>uid</code>, <code>sn</code>, <code>givenName</code>,<code>displayName</code>,<code>cn</code> ,<code>gecos</code> , <code>homeDirectory</code>, and set userPassword to the password generated earlier. Ensure each user has a unique <code>uidNumber</code>, and you can keep the gidNumber the same if users belong to the same primary group</p>
<pre><code>dn: uid=sumit,ou=users,dc=my-flows,dc=site
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: sumit
sn: shinde
givenName: sumit
cn: sumit shinde
displayName: sumit shinde
uidNumber: 1000
gidNumber: 7000
userPassword: {SSHA}uQVjd8MLaJ7AXEd/grqViuKnk9tNojdy
gecos: sumit shinde
loginShell: /bin/bash
homeDirectory: /home/sumit
</code></pre>
</li>
<li>
<p>Save and exit the configuration file.</p>
</li>
<li>
<p>Add the user to the LDAP directory:</p>
<div style="position: relative" id="code-container-242">
<pre class="language-bash"><code id="code-242" class="language-bash">ldapadd <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token parameter variable">-f</span> user.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-242" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ol>
<h4 id="updating-groups-and-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#updating-groups-and-users"></a> Updating Groups and Users</h4>
<ol>
<li>
<p>Create a file for the user update:</p>
<div style="position: relative" id="code-container-253">
<pre class="language-bash"><code id="code-253" class="language-bash"><span class="token function">nano</span> user_update.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-253" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Add the following content to <code>user_update.ldif</code> to update the user's details (e.g., changing the display name):</p>
<pre><code>dn: uid=sumit,ou=users,dc=my-flows,dc=site
changetype: modify
replace: displayName
displayName: Sumit Rupesh Shinde
</code></pre>
</li>
<li>
<p>Save and exit the configuration file.</p>
</li>
<li>
<p>Apply the update to the LDAP directory:</p>
<div style="position: relative" id="code-container-270">
<pre class="language-bash"><code id="code-270" class="language-bash">ldapmodify <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token parameter variable">-f</span> user_update.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-270" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Create a file for the group update:</p>
<div style="position: relative" id="code-container-276">
<pre class="language-bash"><code id="code-276" class="language-bash"><span class="token function">nano</span> group_update.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-276" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Add the following content to <code>group_update.ldif</code> to update the group's details (e.g., changing the organizational unit name):</p>
<pre><code>dn: ou=users,dc=my-flows,dc=site
changetype: modify
replace: ou
ou: staff
</code></pre>
</li>
<li>
<p>Save and exit the configuration file.</p>
</li>
<li>
<p>Apply the update to the LDAP directory:</p>
<div style="position: relative" id="code-container-293">
<pre class="language-bash"><code id="code-293" class="language-bash">ldapmodify <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token parameter variable">-f</span> group_update.ldif</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-293" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ol>
<h4 id="deleting-groups-and-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#deleting-groups-and-users"></a> Deleting Groups and Users</h4>
<ol>
<li>
<p>Delete a user from the LDAP directory:</p>
<div style="position: relative" id="code-container-304">
<pre class="language-bash"><code id="code-304" class="language-bash">ldapdelete <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token string">"uid=sumit,ou=users,dc=my-flows,dc=site"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-304" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
<li>
<p>Delete a group from the LDAP directory:</p>
<div style="position: relative" id="code-container-310">
<pre class="language-bash"><code id="code-310" class="language-bash">ldapdelete <span class="token parameter variable">-x</span> <span class="token parameter variable">-D</span> <span class="token assign-left variable">cn</span><span class="token operator">=</span>admin,dc<span class="token operator">=</span>my-flows,dc<span class="token operator">=</span>site <span class="token parameter variable">-W</span> <span class="token string">"ou=users,dc=my-flows,dc=site"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-310" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ol>
<h3 id="configuring-and-enabling-sso-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#configuring-and-enabling-sso-in-flowfuse"></a> Configuring and Enabling SSO in FlowFuse</h3>
<ol>
<li>To configure FlowFuse with SSO, make sure you are logged in as an administrator.</li>
<li>Go to Admin settings by clicking on the profile icon in the top-right corner and then selecting "Admin settings".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.avif 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.webp 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.jpeg 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the admin settings option in the profile icon" alt=""Screenshot showing the admin settings option in the profile icon"" loading="lazy" decoding="async" src="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.jpeg" width="1300" height="590" /></picture></p>
<ol start="3">
<li>Click on "Settings" from the left sidebar and switch to the SSO section.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-650.avif 650w, https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-650.webp 650w, https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-650.jpeg 650w, https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the sso section in the admin settings" alt=""Screenshot showing the sso section in the admin settings"" loading="lazy" decoding="async" src="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-650.jpeg" width="1300" height="596" /></picture></p>
<ol start="4">
<li>Click on the top-right "Create SSO configuration".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-650.avif 650w, https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-650.webp 650w, https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-650.jpeg 650w, https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the 'create sso configuration' button" alt=""Screenshot showing the 'create sso configuration' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-650.jpeg" width="1300" height="587" /></picture></p>
<ol start="5">
<li>Enter the name for your configuration, then enter the domain with <code>@</code> prefix and select the "LDAP" option. Click on the "Create configuration" button.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sso-config-ldap-8gNWc12Hl4-274.avif 274w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sso-config-ldap-8gNWc12Hl4-274.webp 274w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the initial form to create ldap sso configuration" alt=""Screenshot showing the initial form to create ldap sso configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sso-config-ldap-8gNWc12Hl4-274.jpeg" width="274" height="247" /></picture></p>
<ol start="6">
<li>In the next form, in the server field enter <code>your-server-ip:389</code>. 389 is the default port for LDAP but make sure to check it. If you are going to enable TLS, replace the port with 636.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ldap-advance-config-tag-Wy1vziK3ne-303.avif 303w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ldap-advance-config-tag-Wy1vziK3ne-303.webp 303w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the advance form to create ldap sso configuration" alt=""Screenshot showing the advance form to create ldap sso configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/ldap-advance-config-tag-Wy1vziK3ne-303.jpeg" width="303" height="643" /></picture></p>
<ol start="7">
<li>Enter the the bind DN into the username field.</li>
<li>Enter the password for the LDAP administrator in the password field.</li>
<li>Enter the Base DN. For example, if your domain is <code>my-flows.site</code>, the Base DN will be <code>dc=my-flows,dc=site</code>.</li>
<li>Click on the "Update configuration" button.</li>
</ol>
<h3 id="signing-in-using-sso" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#signing-in-using-sso"></a> Signing in Using SSO</h3>
<p>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 <a href="https://flowfuse.com/docs/admin/user_management/#creating-new-users">creating users in FlowFuse</a>.</p>
<ol>
<li>Open your platform in the browser. Enter the username in the username/email field.</li>
<li>Click on "Login".</li>
<li>Then enter the password set in the LDAP directory for that user.</li>
</ol>
<p><em>Note: Admin users will still be able to log in with their original FlowFuse username/password - this ensures they don't get locked out of the platform if there is a problem with the SSO configuration</em></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/#conclusion"></a> Conclusion</h2>
<p>In this guide, we covered how to set up SSO with LDAP for your self-hosted FlowFuse platform using OpenLDAP. We installed and configured OpenLDAP, learned to managed groups and users, and configured SSO within FlowFuse. This setup enhances security by centralizing user authentication and simplifies access across applications, ensuring efficient user management in your FlowFuse deployment.</p>
<div>
<p>Got questions or need help setting up SSO LDAP for FlowFuse (Node-RED)? with us!</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/contact-us/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20contact%20us&utm_term=high_intent&utm_content=How%20to%20Set%20Up%20SSO%20LDAP%20for%20Node-RED">
Talk to Us Now
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/New Charts Available in FlowFuse DashboardOur most recent update for FlowFuse Dashboard has introduced Pie, Donut and Grouped Bar charts, and plenty more.2024-07-26T00:00:00ZJoe Pavitt<p>It's been a while coming, but we've finally introduced a new set of chart types to FlowFuse Dashboard. We've added Pie, Donut and Grouped (Stacks and Side-by-Side) Bar charts to the UI Chart node. We've also shipped plenty of other improvements and fixes in this release, so let's dive in.</p>
<!--more-->
<h2 id="grouped-bar-charts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#grouped-bar-charts"></a> Grouped Bar Charts</h2>
<p>We now have the option "Group By" available when building Bar Charts, with the options "Stacks" and "Side-by-Side". This allows you to group data in a more meaningful way, and is particularly useful when comparing multiple data sets.</p>
<h3 id="stacks" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#stacks"></a> Stacks</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-bar-grouped-finance-stacks-EwQ-awf3IQ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-bar-grouped-finance-stacks-EwQ-awf3IQ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a stacked bar chart with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-bar-grouped-finance-stacks-EwQ-awf3IQ-1920.jpeg" width="1920" height="806" /></picture>
<em>Screenshot showing a stacked bar chart with Dashboard</em></p>
<h3 id="side-by-side" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#side-by-side"></a> Side-by-Side</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-bar-grouped-finance-h-lRfcMpye-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-bar-grouped-finance-h-lRfcMpye-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing a side-by-side grouped bar chart with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-bar-grouped-finance-h-lRfcMpye-1920.jpeg" width="1920" height="839" /></picture>
<em>Screenshot showing a side-by-side grouped bar chart with Dashboard</em></p>
<h2 id="pie-%26-donut-charts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#pie-%26-donut-charts"></a> Pie & Donut Charts</h2>
<p>Radial charts are now available in Dashboard, with the introduction of Pie and Donut charts. These are particularly useful when you want to show the proportion of a single data set.</p>
<p>If you're using multiple "Series" here, then you'll get multiple nested radial charts, which can be particularly useful when comparing multiple data sets.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-pie-doughnut-VCFli2z4ox-1380.avif 1380w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-pie-doughnut-VCFli2z4ox-1380.webp 1380w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new Pie and Donut charts available with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-pie-doughnut-VCFli2z4ox-1380.jpeg" width="1380" height="666" /></picture>
<em>Screenshot showing the new Pie and Donut charts available with Dashboard</em></p>
<h2 id="general-chart-changes-%26-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#general-chart-changes-%26-improvements"></a> General Chart Changes & Improvements</h2>
<h3 id="breaking-changes---bar-charts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#breaking-changes---bar-charts"></a> Breaking Changes - Bar Charts</h3>
<p>With the introduction of the new chart types, we've generalised the key mapping features for charts. Previously, Bar Charts were using the "Series" property to define how data is rendered onto the x-axis. In hindsight, this didn't really make any sense, it should be the "X" property that defines the data used on the x-axis, so we've corrected that going forward, but it did mean introducing a breaking change. We don't do this lightly, but in this case, it was necessary to ensure the charting experience is consistent across all chart types.</p>
<p>It does however mean that Bar Charts created in <code>1.13.0</code> or below will need to be updated to reflect this. You just need to set "X" to whatever you have in the "Series" property, and then set "Series" to "None".</p>
<h3 id="mapping-your-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#mapping-your-data"></a> Mapping Your Data</h3>
<h4 id="series" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#series"></a> Series</h4>
<p>This property is now, consistently, how you want to group your data. If a new <code>msg</code> is coming in, and a property on that <code>msg</code> defines its group, then you can set "Series" to something like <code>msg.myCategory</code>.</p>
<p>If a single data point needs to plot multiple points/bars onto a chart, then you can use the "JSON" type here, and list the different properties you want to plot, one for each category.</p>
<p>For Radial charts (Pie & Donut), having multiple series would provide multiple, nested radial charts.</p>
<h4 id="x" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#x"></a> X</h4>
<p>With this, we define which property (or <em>properties</em> if a single piece of data needs to plot multiple points/bars) we want to plot on the x-axis.</p>
<p>Note that on Radial charts (Pie & Donut), this is the value that defines the label of the segment.</p>
<h4 id="y" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#y"></a> Y</h4>
<p>Which property should be used to define the y-axis value, i.e. height of the bar or the y-coordinate of a point on a scatter chart.</p>
<p>Note that on Radial charts (Pie & Donut), this is the value that defines the size of the segment.</p>
<h4 id="example%3A-plotting-star-wars-character-heights" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#example%3A-plotting-star-wars-character-heights"></a> Example: Plotting Star Wars Character Heights</h4>
<p>The <a href="https://swapi.dev/">Star Wars API</a> is a free, open API that provides data on the Star Wars universe, an example API call can return details about particular characters, and the response is as follows:</p>
<div style="position: relative" id="code-container-85">
<pre class="language-json"><code id="code-85" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Luke Skywalker"</span><span class="token punctuation">,</span><br /> <span class="token property">"height"</span><span class="token operator">:</span><span class="token string">"172"</span><span class="token punctuation">,</span><br /> <span class="token property">"mass"</span><span class="token operator">:</span><span class="token string">"77"</span><span class="token punctuation">,</span><br /> <span class="token property">"hair_color"</span><span class="token operator">:</span><span class="token string">"blond"</span><span class="token punctuation">,</span><br /> <span class="token property">"skin_color"</span><span class="token operator">:</span><span class="token string">"fair"</span><span class="token punctuation">,</span><br /> <span class="token property">"eye_color"</span><span class="token operator">:</span><span class="token string">"blue"</span><span class="token punctuation">,</span><br /> <span class="token property">"birth_year"</span><span class="token operator">:</span><span class="token string">"19BBY"</span><span class="token punctuation">,</span><br /> <span class="token property">"gender"</span><span class="token operator">:</span><span class="token string">"male"</span><span class="token punctuation">,</span><br /> ...<br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> ...<br /><span class="token punctuation">}</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-85" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>We can configure a chart with the following "Mapping" options:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-bar-sw-config-TALI_10r4s-1566.avif 1566w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-bar-sw-config-TALI_10r4s-1566.webp 1566w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example mapping of data to a UI Chart for data coming from the Star Wars API" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-bar-sw-config-TALI_10r4s-1566.jpeg" width="1566" height="218" /></picture>
<em>Example mapping of data to a UI Chart for data coming from the Star Wars API</em></p>
<p>and this would then be the resulting chart:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-bar-sw-ae8c8goHrJ-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-bar-sw-ae8c8goHrJ-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example bar chart showing heights of Star Wars characters" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-bar-sw-ae8c8goHrJ-1920.jpeg" width="1920" height="783" /></picture>
<em>Example bar chart showing heights of Star Wars characters</em></p>
<p>Note, the chart takes in all of the data sent to it, uses the <code>name</code> field to define where on the x-axis the data should be plotted, and the <code>height</code> field to define the height of the bar (the y-axis).</p>
<p>You can read more about this example, and access the flow itself in our documentation <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html#bar-charts">here</a></p>
<h2 id="live-dashboard-demo" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#live-dashboard-demo"></a> Live Dashboard Demo</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-interactive-docs-ocmrnC7OdX-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-interactive-docs-ocmrnC7OdX-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of our new interactive Dashboard to demonstrate how charts render in FlowFuse Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-interactive-docs-ocmrnC7OdX-1920.jpeg" width="1920" height="1009" /></picture>
<em>Screenshot of our new interactive Dashboard to demonstrate how charts render in FlowFuse Dashboard</em></p>
<p>With the new release also comes a new <a href="https://dashboard-demos.flowfuse.cloud/dashboard/charts-example">Public Demo Dashboard</a> that we've made available for all to use and play with.</p>
<p>You can try it out, and interact with a range of different chart types, including the new Pie, Donut and Grouped Bar charts. We'll also be extending this soon to cover the full range of widgets available in FlowFuse Dashboard.</p>
<p>If you want more technical detail, you can also check out our online documentation <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-chart.html">here</a>.</p>
<h2 id="what-else-is-new%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#what-else-is-new%3F"></a> What else is new?</h2>
<p>You can find the full 1.14.0 Release Notes <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.14.0">here</a>.</p>
<p>Just to highlight a few, particularly valuable, updates and fixes:</p>
<ul>
<li>UI Button Group - Now supports <code>msg.enabled</code> to enable/disable the widget.</li>
<li>UI Form - Now supports <code>msg.enabled</code> to enable/disable the widget.</li>
<li>UI Button - Color customisation now available, without needing to write overriding CSS.</li>
<li>UI Switch - Fix missing <code>msg.topic</code> when setting the topic to a static string.</li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/dashboard-new-charts/#what's-next%3F"></a> What's Next?</h2>
<p>Work has already begun on the next release, <code>1.15.0</code>, you can see what items we have queued up <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">here</a>, if you've got any feedback or suggestions, please do let us know, and feel free to open new issues on our <a href="https://github.com/FlowFuse/node-red-dashboard/issues">GitHub</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/Evolution of Technology: Impact on Job Roles and CompaniesThe Role of FlowFuse in the Modern Technological Landscape2024-07-23T00:00:00ZSumit Shinde<p>Throughout history, technology has continuously transformed industries, job roles, and entire companies. While these changes often evoke fear and resistance, they also create new jobs, opportunities for innovation and growth. This post will explore how historical technological advancements have reshaped both individual job roles and entire companies, examine current trends like AI and low-code tools, and discuss the critical importance of adaptability for both individuals and businesses in navigating technological disruption, Importantly, we will address the pressing question of our time: <em><strong>"How can AI and low-code tools both enhance and challenge human capabilities, creativity, and the job market, and is this the next frontier in manufacturing or just hype"</strong></em></p>
<!--more-->
<h2 id="historical-examples-of-technological-disruption-and-business-impact" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/#historical-examples-of-technological-disruption-and-business-impact"></a> Historical Examples of Technological Disruption and Business Impact</h2>
<p>The Industrial Revolution serves as a powerful example of how technological advancements disrupted entire industries and reshaped business landscapes. In the late 18th and early 19th centuries, mechanized manufacturing replaced traditional artisanal crafts and manual labor. Companies that embraced these new technologies, such as textile mills and steam-powered factories, experienced unprecedented growth and profitability. Conversely, businesses that clung to outdated methods faced decline or closure as they struggled to compete with more efficient and scalable production methods.</p>
<p>The advent of the automobile in the early 20th century brought about another wave of technological disruption. As cars replaced horse-drawn carriages, companies in the transportation and manufacturing sectors had to adapt or risk becoming obsolete. Established businesses that successfully transitioned to automotive manufacturing thrived, while those that resisted change faced significant challenges. For instance, companies that continued to produce horse-drawn carriages struggled to maintain market relevance and profitability.</p>
<p>The digital revolution of the late 20th century further accelerated technological disruption across industries. The introduction of computers, the internet, and digital communication transformed how businesses operated, communicated, and conducted commerce. Companies that embraced digital technologies gained competitive advantages in efficiency, customer engagement, and global market reach. In contrast, businesses that resisted digital transformation struggled to keep pace with rapidly evolving consumer expectations and market dynamics.</p>
<h2 id="current-trends%3A-ai%2C-low-code-tools%2C-and-strategic-adaptation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/#current-trends%3A-ai%2C-low-code-tools%2C-and-strategic-adaptation"></a> Current Trends: AI, Low-Code Tools, and Strategic Adaptation</h2>
<p>Today, we are witnessing a new era of technological disruption driven by artificial intelligence (AI) and low-code/no-code development platforms. AI technologies are revolutionizing business operations by automating routine tasks, analyzing vast amounts of data, and enhancing decision-making processes. Companies that strategically integrate AI into their operations can optimize productivity, personalize customer experiences, and gain valuable insights into market trends and consumer behavior. However, businesses that hesitate to adopt AI risk falling behind competitors who leverage these technologies to innovate and drive business growth.</p>
<p>Low-code and no-code development platforms represent another transformative trend reshaping how companies approach software development and innovation. These platforms empower business users and citizen developers to create applications and automate workflows with minimal coding knowledge. By democratizing software development, low-code tools enable faster application deployment, greater agility in responding to market demands, and enhanced collaboration between IT and business teams. Companies that embrace low-code platforms like Node-RED can accelerate digital transformation initiatives, streamline business processes, and drive innovation across the organization.</p>
<p>Furthermore, AI and low-code tools disrupt traditional job roles and create new opportunities across various fields. Roles such as AI Integration Specialists, Data Scientists, and Machine Learning Engineers, prompt engineers are increasingly in demand as companies seek to enhance efficiency and productivity. Similarly, the adoption of low-code tools like Node-RED is leading to the creation of positions such as Automation Engineers, IoT Developers, and Integration specialists, and Node-RED is becoming a standard requirement in jobs across manufacturing and IoT industries.</p>
<h2 id="the-importance-of-adaptability-and-strategic-vision" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/#the-importance-of-adaptability-and-strategic-vision"></a> The Importance of Adaptability and Strategic Vision</h2>
<p>Historically, individuals and businesses that fail to adapt to technological change or embrace innovation risk becoming obsolete in competitive markets. A notable example is Nokia, once a dominant player in the mobile phone industry. Nokia initially thrived by pioneering mobile technology and establishing a strong market presence. However, the company's reluctance to embrace the shift to smartphones and its adherence to outdated business models ultimately led to its decline. In contrast, competitors like Apple and Samsung seized opportunities in the smartphone market, leveraging innovation and consumer-centric strategies to surpass Nokia in market share and profitability. There are numerous such examples, one being Xerox. Watch this short video where Steve Jobs, founder of Apple, explains <a href="https://www.youtube.com/watch?v=X3NASGb5m8s&t=2s">Why Xerox failed</a>.</p>
<p>The strategic adoption of AI and low-code tools is critical for maintaining competitive advantage and driving sustainable growth. While concerns about AI ethics and reliability are valid, businesses that implement AI technologies responsibly can enhance operational efficiency, improve decision-making processes, and deliver superior customer experiences. Similarly, companies that embrace low-code development platforms can accelerate time-to-market for new applications, reduce development costs, and empower business units to innovate and iterate more rapidly. Amazon, for instance, exemplifies this adaptability by evolving from an online bookstore to a global leader in e-commerce and cloud computing through strategic technology integration.</p>
<h2 id="flowfuse%3A-the-perfect-combo-of-low-code-and-ai" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/#flowfuse%3A-the-perfect-combo-of-low-code-and-ai"></a> FlowFuse: The perfect combo of Low-code and AI</h2>
<p>FlowFuse is a cloud-based platform for Node-RED, a popular low-code tool. It enhances Node-RED with real-time collaboration, version control, and remote edge device programming.</p>
<p>With the capability to integrate over 5000 applications, protocols, and technologies, FlowFuse empowers businesses to innovate and streamline operations.
Recently, FlowFuse has integrated AI features like the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/">Function GPT</a> and <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/chatgpt-gpt/">Node-RED Builder</a> and the <a href="https://flowfuse.com/changelog/2024/07/flowfuse-assistant/">FlowFuse AI Assistant</a>. These tools allow users to automate flow creation, significantly boosting productivity. This combination of low-code development and AI positions FlowFuse as a transformative tool for modern businesses.</p>
<p>Manufacturing companies are already adopting these advanced features to optimize their operations and stay competitive. Tech professionals should also prepare to leverage these tools to harness the full potential of AI and low-code development.</p>
<p>FlowFuse provides businesses, particularly those in the manufacturing sector, with a revolutionary solution powered by artificial intelligence and effective low-code platforms that make operations more efficient and increase output. This combination promotes quick innovation and flexibility, essential for maintaining a competitive edge in the era of Industry 4.0. FlowFuse's strong integration features guarantee the ability to expand successfully and maintain smooth connections across various applications, enabling manufacturing companies to improve their operations, enhance teamwork, and provide outstanding products and services.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/evolution-of-technology-impact-on-job-roles-and-companies/#conclusion"></a> Conclusion</h2>
<p>Technology has always driven change, disrupting traditional models and opening new growth avenues. From the Industrial Revolution to today's AI and low-code tools, adaptability, strategic vision, and innovation remain key to success. By embracing emerging technologies and a forward-thinking mindset, individuals and companies can lead in innovation, customer focus, and sustainable growth in a competitive global economy.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/How to Set Up SSO SAML for Node-REDStep-by-step guide on setting up SSO SAML for your self-hosted FlowFuse platform2024-07-17T00:00:00ZSumit Shinde<p>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 <a href="https://flowfuse.com/docs/admin/sso/ldap/">Setting up LDAP SSO for your Self-Hosted FlowFuse</a>.</p>
<!--more-->
<h2 id="what-is-sso%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#what-is-sso%3F"></a> What is SSO?</h2>
<p>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.</p>
<h2 id="understanding-saml" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#understanding-saml"></a> Understanding SAML</h2>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/SAML-SSO-UkKtoO0hob-1280.avif 1280w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/SAML-SSO-UkKtoO0hob-1280.webp 1280w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the working process of SSO SAML" alt=""Image showing the working process of SSO SAML"" loading="lazy" decoding="async" src="https://flowfuse.com/img/SAML-SSO-UkKtoO0hob-1280.jpeg" width="1280" height="720" /></picture></p>
<ol>
<li>
<p><strong>User Request</strong>: A user attempts to access a service or application that is protected by SAML-based SSO.</p>
</li>
<li>
<p><strong>Redirect to IdP</strong>: The Service Provider (SP), recognizing that the user needs authentication, redirects them to the Identity Provider (IdP).</p>
</li>
<li>
<p><strong>User Authentication</strong>: The IdP prompts the user to authenticate themselves. This authentication can involve various methods, such as username/password, multi-factor authentication (MFA), or other authentication mechanisms supported by the IdP.</p>
</li>
<li>
<p><strong>Generating SAML Assertion</strong>: 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.</p>
</li>
<li>
<p><strong>Sending SAML Assertion</strong>: 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.</p>
</li>
<li>
<p><strong>Assertion Validation</strong>: 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.</p>
</li>
<li>
<p><strong>Granting Access</strong>: 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.</p>
</li>
</ol>
<h2 id="benefits-of-using-sso-in-the-workspace" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#benefits-of-using-sso-in-the-workspace"></a> Benefits of Using SSO in the Workspace</h2>
<ol>
<li>
<p><strong>Improved User Experience</strong>: 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.</p>
</li>
<li>
<p><strong>Simplified Management</strong>: 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.</p>
</li>
<li>
<p><strong>Increased Security</strong>: 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.</p>
</li>
<li>
<p><strong>Enhanced Productivity</strong>: SSO allows employees to log in once and gain access to all necessary applications, saving time and reducing interruptions.</p>
</li>
</ol>
<h2 id="setting-up-sso-for-your-self-hosted-flowfuse-platform" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#setting-up-sso-for-your-self-hosted-flowfuse-platform"></a> Setting up SSO For Your Self-Hosted FlowFuse Platform</h2>
<p>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 <a href="https://www.youtube.com/watch?v=Rc7BT7PDqFs">Google Workspace account created</a> and <a href="https://www.youtube.com/watch?v=JIOaLKsz2R0">verified your domain</a> within Google Workspace. FlowFuse supports other providers for SAML SSO as well; check our <a href="https://flowfuse.com/docs/admin/sso/saml/#providers">documentation on supported providers</a> for details.</p>
<p>If you haven't deployed FlowFuse yet, refer to our <a href="https://flowfuse.com/docs/install/introduction/">documentation on installing FlowFuse</a> or our blog on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/">deploying FlowFuse on Ubuntu with Docker</a>.</p>
<h3 id="configuring-sso-in-your-self-hosted-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#configuring-sso-in-your-self-hosted-flowfuse"></a> Configuring SSO in your Self-Hosted FlowFuse</h3>
<ol>
<li>To configure FlowFuse with SSO, make sure you are logged in as an administrator.</li>
<li>Go to Admin settings by clicking on the profile icon in the top-right corner and then selecting "Admin settings".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.avif 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.webp 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.jpeg 650w, https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the admin settings option in the profile icon" alt=""Screenshot showing the admin settings option in the profile icon"" loading="lazy" decoding="async" src="https://flowfuse.com/img/admin-setting-option-0SDptgbXoq-650.jpeg" width="1300" height="590" /></picture></p>
<ol start="3">
<li>Click on "Settings" from the left sidebar and switch to the SSO section.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1838.avif 1838w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1838.webp 1838w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of platform showing the 'admin settings' 'SSO' tab option" alt=""Screenshot of platform showing the 'admin settings' 'SSO' tab option"" loading="lazy" decoding="async" src="https://flowfuse.com/img/setting's-sso-setting-section-SEVuE2rVzo-1838.jpeg" width="1838" height="844" /></picture></p>
<ol start="4">
<li>Click on the top-right "Create SSO configuration".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1840.avif 1840w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1840.webp 1840w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of platform showing the 'Creating configuration' button" alt=""Screenshot of platform showing the 'Creating configuration' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/create-sso-config-button-q823dQy9CT-1840.jpeg" width="1840" height="831" /></picture></p>
<ol start="5">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sso-config-initial-form-sh74vKHgys-387.avif 387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sso-config-initial-form-sh74vKHgys-387.webp 387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of platform form for creating sso configuration" alt=""Screenshot of platform form for creating sso configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sso-config-initial-form-sh74vKHgys-387.jpeg" width="387" height="336" /></picture></p>
<ol start="5">
<li>In the next tab, you'll find the ACS URL and Entity ID. Copy and save them somewhere in the notepad.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sso-config-next-form-2fCVRNz5q0-345.avif 345w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sso-config-next-form-2fCVRNz5q0-345.webp 345w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of advance sso form showing the ACS URL and Entity ID and other feilds for configuration" alt=""Screenshot of advance sso form showing the ACS URL and Entity ID and other feilds for configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sso-config-next-form-2fCVRNz5q0-345.jpeg" width="345" height="668" /></picture></p>
<ol start="6">
<li>Tick "Manage roles using group assertions" if needed. If enabled, keep the Group Assertion Name to its default value and select the team scope:
<ul>
<li>"Apply to selected teams" restricts management to the provided list of groups, For adding the list refer to <a href="https://flowfuse.com/docs/admin/sso/saml/#saml-groups-configuration">SAML Groups configuration</a>, suitable for shared-tenancy platforms like FlowFuse Cloud.</li>
<li>"Apply to all teams" allows SAML groups to manage all teams, suitable for a self-hosted installation with a single SSO configuration.</li>
</ul>
</li>
<li>Click on "Update configuration", we will back to update this configuration with our configuration provided by our Google workpsace later.</li>
</ol>
<h3 id="creating-the-saml-app-in-google-workspace." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#creating-the-saml-app-in-google-workspace."></a> Creating the SAML APP in Google Workspace.</h3>
<ol>
<li>Open the main menu from the top-right corner by clicking on menu icon.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/main-menu's-web-and-mobile-apps-option-vTMWdNt0Mh-1832.avif 1832w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/main-menu's-web-and-mobile-apps-option-vTMWdNt0Mh-1832.webp 1832w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'web and mobile apps' option in the sidebar in the Google workspace admin console" alt=""Screenshot showing the 'web and mobile apps' option in the sidebar in the Google workspace admin console"" loading="lazy" decoding="async" src="https://flowfuse.com/img/main-menu's-web-and-mobile-apps-option-vTMWdNt0Mh-1832.jpeg" width="1832" height="835" /></picture></p>
<ol start="2">
<li>
<p>Click on the "Apps" icon, which expands additional choices. Then click on the "Web and mobile apps."</p>
</li>
<li>
<p>Then the new tab opens, click on to the "Add app" from the top.</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/add-custom-saml-apps-option-1q-zNodeb5-1842.avif 1842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/add-custom-saml-apps-option-1q-zNodeb5-1842.webp 1842w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Add custom SAML apps' option in the Google workspace admin console" alt=""Screenshot showing the 'Add app' and 'Add custom SAML apps' option in the Google workspace admin console"" loading="lazy" decoding="async" src="https://flowfuse.com/img/add-custom-saml-apps-option-1q-zNodeb5-1842.jpeg" width="1842" height="835" /></picture></p>
<ol start="4">
<li>The options open select the "Add custom SAML apps."</li>
<li>Enter the app name description and upload an icon for the app.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-to-add-app-info-WpCyLpFuCJ-1853.avif 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-to-add-app-info-WpCyLpFuCJ-1853.webp 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the form asking for app information for we are going to create the SAML app configuration in the Google workspace admin console" alt=""Screenshot showing the form asking for app information for we are going to create the SAML app configuration in the Google workspace admin console"" loading="lazy" decoding="async" src="https://flowfuse.com/img/form-to-add-app-info-WpCyLpFuCJ-1853.jpeg" width="1853" height="829" /></picture></p>
<ol start="6">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/config-details-for-sso-rz5e1zX_vu-1841.avif 1841w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/config-details-for-sso-rz5e1zX_vu-1841.webp 1841w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot configuration provided the Google workspace admin console for SSO SAML" alt=""Screenshot configuration provided the Google workspace admin console for SSO SAML"" loading="lazy" decoding="async" src="https://flowfuse.com/img/config-details-for-sso-rz5e1zX_vu-1841.jpeg" width="1841" height="835" /></picture></p>
<ol start="7">
<li>Click on the continue.</li>
<li>In the next tab, enter the ACS URL and entity id provided by your self-hosted flowfuse platform.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-to-add-the-config-provided-flowfuse-ni5njs_-9T-1845.avif 1845w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-to-add-the-config-provided-flowfuse-ni5njs_-9T-1845.webp 1845w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot form asking for the configuration detailes provided by your self-hosted FlowFuse" alt=""Screenshot form asking for the configuration detailes provided by your self-hosted FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/form-to-add-the-config-provided-flowfuse-ni5njs_-9T-1845.jpeg" width="1845" height="832" /></picture></p>
<ol start="9">
<li>
<p>Enter the start URL as <code>forge.<your-domain>.com</code>, we are specify that after successfull sign in the user should be redirected to this url.</p>
</li>
<li>
<p>Next, click on "Continue" button and then click on "finish".</p>
</li>
</ol>
<h3 id="updating-the-flowfuse-sso-configuration-and-enabling-it" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#updating-the-flowfuse-sso-configuration-and-enabling-it"></a> Updating the FlowFuse SSO configuration and enabling it</h3>
<p>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.</p>
<ol>
<li>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."</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-sso-config-sAwX1tCQHd-1846.avif 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-sso-config-sAwX1tCQHd-1846.webp 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing three dot icons in your self-hosted FlowFuse" alt=""Screenshot showing three dot icons in your self-hosted FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-sso-config-sAwX1tCQHd-1846.jpeg" width="1846" height="846" /></picture></p>
<ol start="2">
<li>Add the copied values for the fields: Identity Provider Single Sign-On URL, Identity Provider Issuer ID / URL, and X.509 Certificate Public Key provided by the provider. I instructed you in the above section to copy or download them.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sso-config-form-os1HEm-6cT-378.avif 378w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sso-config-form-os1HEm-6cT-378.webp 378w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the form updated with configuration provided by Google workspace" alt=""Screenshot showing the form updated with configuration provided by Google workspace"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sso-config-form-os1HEm-6cT-378.jpeg" width="378" height="624" /></picture></p>
<ol start="3">
<li>Click on the "Active" option to enable the SSO in the same form.</li>
<li>Click on "Update configuration".</li>
</ol>
<h3 id="signing-in-using-sso" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#signing-in-using-sso"></a> Signing in Using SSO</h3>
<p>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 <a href="https://flowfuse.com/docs/admin/user_management/#creating-new-users">creating users in FlowFuse</a>. Additionally, the user must already be logged in with that email in the browser.</p>
<ol>
<li>Open your platform in the browser, Enter the email address in the username/email field.</li>
<li>Click on "Login".</li>
<li>A Google tab will open, displaying the email addresses you are signed in with. Select the email address you entered into the username/email field.</li>
</ol>
<p><em>Note: Admin users will still be able to log in with their original FlowFuse username/password - this ensures they don't get locked out of the platform if there is a problem with the SSO configuration</em></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Got questions or need help setting up SSO SAML for FlowFuse (Node-RED)?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/contact-us/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20contact%20us&utm_term=high_intent&utm_content=How%20to%20Set%20Up%20SSO%20SAML%20for%20Node-RED">
Talk to Us Now
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/Building on FlowFuse: Remote Device MonitoringIn 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.2024-07-17T00:00:00ZJoe Pavitt<p>FlowFuse has established a rich ecosystem of products to help you build bespoke, powerful, low-code applications.</p>
<p>We've seen customers utilizing these to <a href="https://flowfuse.com/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing/">revolutionise precision manufacturing</a>, <a href="https://flowfuse.com/customer-stories/node-red-building-management/">automate building management</a> and <a href="https://flowfuse.com/customer-stories/un-wmo-nr-data-sharing/">modernize the distribution of global weather data</a>, just to name a few examples.</p>
<p>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 <strong>"Remote Device Monitoring"</strong>.</p>
<!--more-->
<h2 id="use-case" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#use-case"></a> Use Case</h2>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-monitoring-use-case-9ilml0IOPW-1056.avif 1056w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-monitoring-use-case-9ilml0IOPW-1056.webp 1056w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Diagram showing the breakdown of each component that makes up a "Device Monitoring" use case." loading="lazy" decoding="async" src="https://flowfuse.com/img/device-monitoring-use-case-9ilml0IOPW-1056.jpeg" width="1056" height="550" /></picture>
<em>Diagram showing the breakdown of each component that makes up a "Device Monitoring" use case.</em></p>
<p>Breaking it down we have the following fundamental requirements:</p>
<ul>
<li><strong>Read:</strong> Reading data from the devices using the relevant protocol</li>
<li><strong>Parse:</strong> Process the data into a computational format</li>
<li><strong>Compute/Monitor:</strong> Analysis & monitoring of the data from all devices</li>
<li><strong>Action:</strong> Taking action based on the data from the devices</li>
</ul>
<p>Then, we have to consider that this functionality needs to be deployed out to thousands of devices. So, our final requirement is:</p>
<ul>
<li><strong>Maintain:</strong> 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.</li>
</ul>
<h2 id="architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#architecture"></a> Architecture</h2>
<p>Let's consider an example architecture set in an automotive plant:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-monitoring-architecture-R5RLVV-e7Z-1740.avif 1740w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-monitoring-architecture-R5RLVV-e7Z-1740.webp 1740w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Diagram showing the architecture of a "Device Monitoring" use case in an automotive plant." loading="lazy" decoding="async" src="https://flowfuse.com/img/device-monitoring-architecture-R5RLVV-e7Z-1740.jpeg" width="1740" height="1400" /></picture>
<em>Diagram showing an example architecture of a "Device Monitoring" use case in an automotive plant.</em></p>
<ul>
<li><strong>Laser Welding Machine:</strong> 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.</li>
<li><strong>Body Shop:</strong> 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.</li>
<li><strong>Plant:</strong> 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.</li>
<li><strong>Company IT Dept:</strong> 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.</li>
<li><strong>Cloud:</strong> 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.</li>
</ul>
<p>This diagram also demonstrates a sample of the rich ecosystem of communication protocols that Node-RED can support:</p>
<ul>
<li><strong>TCP:</strong> Read and process data from from bespoke and custom devices</li>
<li><strong>EtherNet/IP:</strong> Collect data from Allen Bradley and other EtherNet/IP devices</li>
<li><strong>MQTT:</strong> Modern devices and factory generated IIoT data</li>
<li><strong>OPC-UA:</strong> Communicate with newer PLCs and OPC Servers</li>
<li><strong>Modbus:</strong> Data collection from existing Modbus enabled devices like Temperature Probes, Invertors, Encoders</li>
<li><strong>MC Protocol:</strong> Gather data from Mitsubishi PLCs</li>
<li><strong>FINS:</strong> Gather data from OMRON PLCs</li>
<li><strong>Siemens S7:</strong> Gather data from Siemens PLCs using the S7 Protocol</li>
</ul>
<h3 id="flowfuse-technology-stack" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#flowfuse-technology-stack"></a> FlowFuse Technology Stack</h3>
<p>Given the above architecture, let's take a look at the relevant FlowFuse offerings and see what they're contributing:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ff-ecosystem-lineup-aEBXVjmA5h-1118.avif 1118w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/ff-ecosystem-lineup-aEBXVjmA5h-1118.webp 1118w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Lineup of each of the FlowFuse offerings" loading="lazy" decoding="async" src="https://flowfuse.com/img/ff-ecosystem-lineup-aEBXVjmA5h-1118.jpeg" width="1118" height="273" /></picture></p>
<ul>
<li><strong>Node-RED:</strong> 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.</li>
<li><strong>FlowFuse Dashboard:</strong> 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.</li>
<li><strong>FlowFuse:</strong> 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.</li>
<li><strong>FlowFuse Device Agent:</strong> 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.</li>
<li><strong>FlowFuse Project Nodes:</strong> 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.</li>
</ul>
<h2 id="what-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#what-next%3F"></a> What Next?</h2>
<h3 id="deploy-to-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#deploy-to-flowfuse"></a> Deploy to FlowFuse</h3>
<p>If you're interested in discussing how your company could benefit from this design pattern, please do <a href="https://flowfuse.com/contact-us/?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</a>.</p>
<h3 id="customer-stories" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/building-on-flowfuse-devices/#customer-stories"></a> Customer Stories</h3>
<p>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:</p>
<ul class="grid grid-cols-1 sm:grid-cols-2 gap-4 px-0 list-none">
<li class="customer-story-tile w-full my-2 border px-0 rounded-lg hover:drop-shadow-lg hover:border-blue-600 transition ease-in-out duration-300 bg-white">
<a href="https://flowfuse.com/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing" class="w-full flex flex-col group hover:no-underline h-full m-0">
<div class="">
<div>
<p></p><div class="relative border-b">
<div class="w-full h-52 sm:h-48 ff-image-cover ff-image-top-rounded"><p></p>
<p><picture>
<img src="https://flowfuse.com/images/stories/abrasive_tech.jpg" alt="Image representing Revolutionizing Precision Manufacturing with Node-RED" />
</picture>
</p></div><p></p>
<p></p></div><p></p>
<p></p></div>
<div class="flex flex-col mt-1 mb-0 p-5 pt-3 gap-2">
<label class="font-bold"><span class="text-gray-400">Abrasive Technology</span></label>
<h3 class="group-hover:text-blue-600 font-medium m-0 mt-0 mb-2" style="line-height: 1.6rem"><span class="text-lg">Revolutionizing Precision Manufacturing with Node-RED</span></h3>
</div>
</div>
</a><p></p>
</li>
<li class="customer-story-tile w-full my-2 border px-0 rounded-lg hover:drop-shadow-lg hover:border-blue-600 transition ease-in-out duration-300 bg-white">
<a href="https://flowfuse.com/customer-stories/leveraging-node-red-and-flowfuse-to-revolutionize-irrigation" class="w-full flex flex-col group hover:no-underline h-full m-0">
<div class="">
<div>
<p></p><div class="relative border-b">
<div class="w-full h-52 sm:h-48 ff-image-cover ff-image-top-rounded"><p></p>
<p><picture>
<img src="https://flowfuse.com/images/stories/pidd-view.png" alt="Image representing Leveraging Node-RED and FlowFuse to Revolutionize Irrigation" />
</picture>
</p></div><p></p>
<p></p></div><p></p>
<p></p></div>
<div class="flex flex-col mt-1 mb-0 p-5 pt-3 gap-2">
<label class="font-bold"><span class="text-gray-400">Paloma Irrigation and Drainage District</span></label>
<h3 class="group-hover:text-blue-600 font-medium m-0 mt-0 mb-2" style="line-height: 1.6rem"><span class="text-lg">Leveraging Node-RED and FlowFuse to Revolutionize Irrigation</span></h3>
</div>
</div>
</a><p></p>
</li>
</ul>../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/Deploying FlowFuse with Docker on an Ubuntu serverStep-by-step guide on how to deploy FlowFuse with Docker on Ubuntu server2024-07-15T00:00:00ZSumit Shinde<p>With Node-RED's increasing role in IoT, FlowFuse Cloud has become a favored platform for deploying production Node-RED applications. It offers <a href="https://flowfuse.com/product/features/">extensive features</a> at a low cost, reducing operational overhead. However, the cloud is not the only option we provide; we also offer a self-hosted option for users who prefer to deploy FlowFuse on their servers. This guide demonstrates how to deploy FlowFuse on your Ubuntu server using Docker, covering key aspects such as domain setup, email, SSL, and more for real-world production scenarios</p>
<!--more-->
<p><em>Note: While the approach provided in this article is an older method for deploying FlowFuse and still works, I recommend following the newer, simpler, and quicker approach. For more details, refer to the official <a href="https://flowfuse.com/docs/install/docker/">Docker documentation</a>.</em></p>
<h2 id="what-is-docker%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#what-is-docker%3F"></a> What is Docker?</h2>
<p><a href="https://docs.docker.com/guides/docker-concepts/the-basics/what-is-an-image/">Docker</a> is an open-source platform that simplifies how applications are deployed, scaled, and managed through containerization. It enables you to package all components required by your project such as code, libraries, and dependencies into a single, portable unit known as a <a href="https://docs.docker.com/guides/docker-concepts/the-basics/what-is-a-container/">Docker container</a>. These containers ensure consistency in application environments and ease deployment by ensuring that applications run predictably across different computing environments, whether on a developer's laptop, a server, or a cloud platform.</p>
<h2 id="deploying-flowfuse-on-ubuntu-server-with-docker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#deploying-flowfuse-on-ubuntu-server-with-docker"></a> Deploying FlowFuse on Ubuntu server with Docker</h2>
<p>Before proceeding, ensure you have your domain and a server with Ubuntu installed, and Docker is installed on it. For more information, refer to <a href="https://docs.docker.com/compose/install/">Docker Compose Installation</a>. Furthermore, If your server has Ubuntu installed without a GUI, you can connect to it from your computer using <a href="https://itsfoss.com/set-up-ssh-ubuntu/">SSH</a>, allowing you to run commands from your local computer on the server.</p>
<h3 id="adding-dns-records-for-your-domain" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#adding-dns-records-for-your-domain"></a> Adding DNS records for your domain</h3>
<p>To make your application accessible on the internet via your domain name, adding DNS records is crucial. These records serve as a vital link between your domain name (like example.com) and your server's IP address. They ensure that when users type your domain into their browsers, they're directed to the correct location where your application is hosted.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-650.avif 650w, https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-650.webp 650w, https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-650.jpeg 650w, https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of panel to add dns records provided by my domain provider" alt=""Screenshot of panel to add dns records provided by my domain provider"" loading="lazy" decoding="async" src="https://flowfuse.com/img/domain-provider-panel-s-lut-slmt-650.jpeg" width="1300" height="539" /></picture></p>
<ol>
<li>
<p>Log in to your Domain Provider's Panel and access the DNS settings of the domain you want to use for the FlowFuse platform.</p>
</li>
<li>
<p>Add entry first entry type: "A", enter "@" into the name field (it points to the domain name itself), and the public IP of your Ubuntu server into the data field.</p>
</li>
<li>
<p>Add entry second entry type: "A", enter "*" into the name field (adding * serves as the <a href="https://docs.digitalocean.com/glossary/wildcard-record/">wildcard domain entry</a> to the IP address of the host running Docker, we are doing this because the FlowFuse application will be hosted on the <code>forge.your-domain-name.com</code> and when creating instances for each, our FlowFuse application will use unique subdomain ) and public IP of your Ubuntu server into the data field.</p>
</li>
<li>
<p>Add the last entry type: "A", and enter "www" into the name field (we are adding this because previously domain names used to have the www prefix) and the public IP of your Ubuntu server into the data field.</p>
</li>
</ol>
<h3 id="installing-and-configuring-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#installing-and-configuring-flowfuse"></a> Installing and configuring FlowFuse</h3>
<p>FlowFuse uses Docker Compose to install and manage the required components. We have built and maintaining that Docker Compose project.</p>
<ol>
<li>Download the latest release <code>tar.gz</code> from our <a href="https://github.com/FlowFuse/docker-compose/releases/latest">Docker Compose project</a>.</li>
</ol>
<div style="position: relative" id="code-container-63">
<pre class="language-bash"><code id="code-63" class="language-bash"><span class="token builtin class-name">cd</span> /opt</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-63" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-64">
<pre class="language-bash"><code id="code-64" class="language-bash"><span class="token function">wget</span> <span class="token operator"><</span>link of the latest tar.gz release<span class="token operator">></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-64" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Unpack this release with the following command. Make sure to replace the release name with the actual release name that you downloaded:</li>
</ol>
<div style="position: relative" id="code-container-72">
<pre class="language-bash"><code id="code-72" class="language-bash"> <span class="token function">tar</span> <span class="token parameter variable">-xvzf</span> vx.x.x.tar.gz</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-72" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Enter the folder with the following command. Again, don't forget to replace the release name with the actual release name that you downloaded:</li>
</ol>
<div style="position: relative" id="code-container-80">
<pre class="language-bash"><code id="code-80" class="language-bash"> <span class="token builtin class-name">cd</span> docker-compose-x.x.x</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-80" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Now, update the FlowFuse configuration file <code>flowforge.yml</code> with your domain name. Currently, it is configured with <code>example.com</code>. To update it quickly, use the following command:</li>
</ol>
<div style="position: relative" id="code-container-88">
<pre class="language-bash"><code id="code-88" class="language-bash"> <span class="token function">sed</span> <span class="token parameter variable">-i</span> <span class="token string">'s/example.com/<replace-with-your-domain-name>/g'</span> ./etc/flowforge.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-88" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Next, update the Docker Compose configuration file <code>docker-compose.yml</code> with your domain:</li>
</ol>
<div style="position: relative" id="code-container-96">
<pre class="language-bash"><code id="code-96" class="language-bash"> <span class="token function">sed</span> <span class="token parameter variable">-i</span> <span class="token string">'s/example.com/<replace-with-your-domain-name>/g'</span> ./docker-compose.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-96" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/docker-compose-update-E7PH4jvha--533.avif 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/docker-compose-update-E7PH4jvha--533.webp 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot the terminal showing the updates made in docker-compose.yml file" alt=""Screenshot the terminal showing the updates made in docker-compose.yml file"" loading="lazy" decoding="async" src="https://flowfuse.com/img/docker-compose-update-E7PH4jvha--533.jpeg" width="533" height="301" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/docker-compose-update-2-Ai-Ux8h9Rd-534.avif 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/docker-compose-update-2-Ai-Ux8h9Rd-534.webp 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot the terminal showing the updates made in 'docker-compose.yml' file" alt=""Screenshot the terminal showing the updates made in 'docker-compose.yml' file"" loading="lazy" decoding="async" src="https://flowfuse.com/img/docker-compose-update-2-Ai-Ux8h9Rd-534.jpeg" width="534" height="304" /></picture></p>
<p>The <code>flowforge.yml</code> file was updated to include our domain in key fields: <code>domain</code>, <code>base_url</code>, and <code>broker.public_url</code>. These adjustments ensure that instance names on Docker platforms incorporate your domain, provide accurate URLs for accessing the platform, and specify the correct URL for devices to connect to the broker if different from <code>broker.url</code>. Additionally, in the <code>docker-compose.yml</code> file, we configured <code>VIRTUAL_HOST</code> and <code>LETSENCRYPT_HOST</code> to reflect our domain.</p>
<p>For more details on these configuration changes, refer to the <a href="https://flowfuse.com/docs/install/configuration/#configuring-flowfuse">documentation</a>.</p>
<h3 id="securing-communication-with-ssl" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#securing-communication-with-ssl"></a> Securing Communication with SSL</h3>
<p>Securing communication with <a href="https://www.youtube.com/watch?v=SJJmoDZ3il8">SSL (Secure Sockets Layer)</a> is crucial for protecting data transmitted between your users and the server. Adding SSL requires obtaining a certificate from a trusted certificate authority (CA) and configuring your server to use this certificate. Configuring SSL manually can be a headache, so we have provided a setup you need to enable.</p>
<ol>
<li>Open the Docker Compose file in your editor:</li>
</ol>
<div style="position: relative" id="code-container-122">
<pre class="language-bash"><code id="code-122" class="language-bash"> <span class="token function">nano</span> docker-compose.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-122" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Uncomment the following lines by removing the <code>#</code> symbol:</li>
</ol>
<div style="position: relative" id="code-container-130">
<pre class="language-yaml"><code id="code-130" class="language-yaml"> <span class="token comment"># - "./certs:/etc/nginx/certs"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-130" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-131">
<pre class="language-yaml"><code id="code-131" class="language-yaml"> <span class="token comment"># - "443:443"</span><br /> <span class="token comment"># environment:</span><br /> <span class="token comment"># - "HTTPS_METHOD=redirect"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-131" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-132">
<pre class="language-yaml"><code id="code-132" class="language-yaml"> <span class="token comment"># acme:</span><br /> <span class="token comment"># image: nginxproxy/acme-companion</span><br /> <span class="token comment"># restart: always</span><br /> <span class="token comment"># volumes:</span><br /> <span class="token comment"># - "/var/run/docker.sock:/var/run/docker.sock:ro"</span><br /> <span class="token comment"># - "./acme:/etc/acme.sh"</span><br /> <span class="token comment"># volumes_from:</span><br /> <span class="token comment"># - nginx:rw</span><br /> <span class="token comment"># environment:</span><br /> <span class="token comment"># - "DEFAULT_EMAIL=mail@example.com"</span><br /> <span class="token comment"># depends_on:</span><br /> <span class="token comment"># - "nginx"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-132" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Update the lines with your domain and email associated with the domain, then save the file:</li>
</ol>
<div style="position: relative" id="code-container-140">
<pre class="language-yaml"><code id="code-140" class="language-yaml"> <span class="token punctuation">-</span> <span class="token string">"DEFAULT_EMAIL=your-email@example.com"</span><br /><br /> <span class="token punctuation">-</span> <span class="token string">"LETSENCRYPT_HOST=mqtt.yourdomain.com"</span><br /><br /> <span class="token punctuation">-</span> <span class="token string">"LETSENCRYPT_HOST=forge.yourdomain.com"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-140" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-650.avif 650w, https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-650.webp 650w, https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-650.jpeg 650w, https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot the terminal showing the update made in `docker-compose.yml` file for ssl configuration" alt=""Screenshot the terminal showing the update made in `docker-compose.yml` file for ssl configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/docker-compose-yml-acme-update-8gOVb6pGkF-650.jpeg" width="1300" height="546" /></picture></p>
<ol start="4">
<li>Open the <code>flowforge.yml</code> file in your editor:</li>
</ol>
<div style="position: relative" id="code-container-151">
<pre class="language-bash"><code id="code-151" class="language-bash"> <span class="token function">nano</span> /etc/flowforge.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-151" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="5">
<li>Update the <code>base_url</code> to start with <code>https://</code> instead of <code>http://</code> and the <code>broker.public_url</code> entry to start with <code>wss://</code> instead of <code>ws://</code>, then save the file.</li>
</ol>
<div style="position: relative" id="code-container-159">
<pre class="language-yaml"><code id="code-159" class="language-yaml"> base_url<span class="token punctuation">:</span> https<span class="token punctuation">:</span>//yourdomain.com<br /><br /> broker<span class="token punctuation">:</span><br /> public_url<span class="token punctuation">:</span> wss<span class="token punctuation">:</span>//mqtt.yourdomain.com</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-159" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-650.avif 650w, https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-650.webp 650w, https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-650.jpeg 650w, https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the terminal showing the update made in 'broker's public_url' in the `flowforge.yml` file for SSL configuration" alt=""Screenshot of the terminal showing the update made in 'broker's public_url' in the `flowforge.yml` file for SSL configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowforge-yml-https-update-qa3fHAbFaJ-650.jpeg" width="1300" height="544" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-650.avif 650w, https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-650.webp 650w, https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-650.jpeg 650w, https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the terminal showing the update made in 'base_url' in the `flowforge.yml` file for SSL configuration" alt=""Screenshot of the terminal showing the update made in 'base_url' in the `flowforge.yml` file for SSL configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowforge-yml-https-update2-QaBS2Rxw5D-650.jpeg" width="1300" height="545" /></picture></p>
<p>Now, when we start our application the acme container will also start and will generate the certificates with let's encrypt on demand for the forge app and then for each of the instances as they are started.</p>
<h3 id="configuring-flowfuse-to-enable-and-use-the-email-feature" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#configuring-flowfuse-to-enable-and-use-the-email-feature"></a> Configuring FlowFuse to Enable and Use the Email Feature</h3>
<p>FlowFuse platform allows you to send invitations to other users within the platform and via email. It also supports receiving critical alerts and resetting passwords through email. To use these features, you need to enable and configure email in FlowFuse with your email address. Before you begin, make sure you have an email ID with an app password. FlowFuse supports Gmail and Outlook emails.</p>
<h4 id="creating-an-app-password-for-your-email" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#creating-an-app-password-for-your-email"></a> Creating an App Password for Your Email</h4>
<p>If you're unfamiliar with generating an app password, watch these helpful videos:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=hXiPshHn9Pw">Creating a Gmail app password</a></li>
<li><a href="https://www.youtube.com/watch?v=5ukSRLRDQIw">Creating an Outlook app password</a></li>
</ul>
<h4 id="enabling-and-configuring-email-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#enabling-and-configuring-email-in-flowfuse"></a> Enabling and Configuring Email in FlowFuse</h4>
<ol>
<li>Open the <code>flowforge.yml</code> config file in your editor:</li>
</ol>
<div style="position: relative" id="code-container-203">
<pre class="language-bash"><code id="code-203" class="language-bash"> <span class="token function">nano</span> /etc/flowforge.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-203" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Update the email configuration section with your email details:</li>
</ol>
<p><strong>For Gmail:</strong></p>
<div style="position: relative" id="code-container-214">
<pre class="language-yaml"><code id="code-214" class="language-yaml"><span class="token key atrule">email</span><span class="token punctuation">:</span><br /> enabled<span class="token punctuation">:</span> <span class="token boolean important">true</span><br /> debug<span class="token punctuation">:</span> <span class="token boolean important">false</span><br /> smtp<span class="token punctuation">:</span><br /> host<span class="token punctuation">:</span> smtp.gmail.com<br /> port<span class="token punctuation">:</span> <span class="token number">465</span><br /> secure<span class="token punctuation">:</span> <span class="token boolean important">true</span><br /> auth<span class="token punctuation">:</span><br /> user<span class="token punctuation">:</span> your<span class="token punctuation">-</span>email@gmail.com<br /> pass<span class="token punctuation">:</span> your<span class="token punctuation">-</span>app<span class="token punctuation">-</span>password</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-214" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><strong>For Outlook:</strong></p>
<div style="position: relative" id="code-container-218">
<pre class="language-yml"><code id="code-218" class="language-yml"><span class="token key atrule">email</span><span class="token punctuation">:</span><br /> enabled<span class="token punctuation">:</span> <span class="token boolean important">true</span><br /> debug<span class="token punctuation">:</span> <span class="token boolean important">false</span><br /> smtp<span class="token punctuation">:</span><br /> host<span class="token punctuation">:</span> smtp.office365.com<br /> port<span class="token punctuation">:</span> <span class="token number">587</span><br /> secure<span class="token punctuation">:</span> <span class="token boolean important">false</span><br /> tls<span class="token punctuation">:</span><br /> ciphers<span class="token punctuation">:</span> <span class="token string">"SSLv3"</span><br /> rejectUnauthorized<span class="token punctuation">:</span> <span class="token boolean important">false</span><br /> auth<span class="token punctuation">:</span><br /> user<span class="token punctuation">:</span> your<span class="token punctuation">-</span>email@outlook.com<br /> pass<span class="token punctuation">:</span> your<span class="token punctuation">-</span>app<span class="token punctuation">-</span>password</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-218" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="running-flowfuse-application" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#running-flowfuse-application"></a> Running FlowFuse Application</h3>
<p>We have completed the basic production-level configuration for running the FlowFuse application. Before running it, we need to ensure that we have the <code>flowfuse/node-red</code> container downloaded, which will be used as the default Node-RED stack.</p>
<ol>
<li>To download the Node-RED container, run the following command:</li>
</ol>
<div style="position: relative" id="code-container-232">
<pre class="language-bash"><code id="code-232" class="language-bash"> <span class="token function">docker</span> pull flowfuse/node-red</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-232" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Now, to run the FlowFuse application, execute the following command:</li>
</ol>
<div style="position: relative" id="code-container-240">
<pre class="language-bash"><code id="code-240" class="language-bash"> <span class="token function">docker</span> compose <span class="token parameter variable">-p</span> flowforge up <span class="token parameter variable">-d</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-240" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>If you see an output similar to the following image, it indicates that all containers that are required for the flowfuse application to run correctly are running.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowforge-yml-successfully-running-container-HVgq7mqAWC-533.avif 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowforge-yml-successfully-running-container-HVgq7mqAWC-533.webp 533w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the terminal showing the all containers are running successfully that are required for the flowfuse to run" alt=""Screenshot of the terminal showing the all containers are running successfully that are required for the flowfuse to run"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowforge-yml-successfully-running-container-HVgq7mqAWC-533.jpeg" width="533" height="329" /></picture></p>
<p>You can now access your self-hosted FlowFuse platform on the internet using the URL <code>forge.<yourdomain>.com</code>, and if your website shows the <code>https</code> as following that means the SSL configurations are also correct.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-650.avif 650w, https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-650.webp 650w, https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-650.jpeg 650w, https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the browser window with flowfuse platform opened" alt=""Screenshot of the browser window with flowfuse platform opened"" loading="lazy" decoding="async" src="https://flowfuse.com/img/deploying-flowfuse-successfull-with-ssl-_flqdn4S-l-650.jpeg" width="1300" height="661" /></picture></p>
<h3 id="setting-up-the-flowfuse-platform" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#setting-up-the-flowfuse-platform"></a> Setting up the FlowFuse Platform</h3>
<p>When you open the platform in your browser for the first time, you'll need to create an administrator account and perform initial configurations:</p>
<ol>
<li>
<p>Open the platform in your browser.</p>
</li>
<li>
<p>Click on the "START SETUP" button.</p>
</li>
<li>
<p>Enter the username, full name, email, and password, and confirm the password to the administrator user account. This first user will have full access to the platform, allowing them to configure settings, and manage users and teams.</p>
</li>
<li>
<p>Next, If you intend to use the FlowFuse Enterprise Edition, enter your license details.</p>
</li>
<li>
<p>Alternatively, you can continue with the FlowFuse Community Edition (CE), which is free, by clicking "Continue with FlowFuse CE".</p>
</li>
</ol>
<h2 id="additional-resources" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/deploying-flowfuse-with-docker/#additional-resources"></a> Additional resources</h2>
<ul>
<li><a href="https://flowfuse.com/docs/install/docker/">Deploying FlowFuse with Docker Documentation</a>: This documentation covers everything in detail on how to install FlowFuse using Docker.</li>
<li><a href="https://www.youtube.com/watch?v=qQwAPuz9bEk">Deploying FlowFuse with Docker on Ubuntu youtube video</a>: This YouTube video demonstrates how to deploy FlowFuse using Docker on an Ubuntu server for your server's local network.</li>
<li><a href="https://flowfuse.com/docs/install/introduction/#do-you-need-help%3F-installation-service">Form for requesting Installation Service</a>: Fill this form if you need assistance with the installation process.</li>
</ul>
<div>
<p>Got questions or need help deploying FlowFuse (Node-RED) on your Ubuntu server?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://flowfuse.com/contact-us/?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta%20contact%20us&utm_term=high_intent&utm_content=Deploying%20FlowFuse%20with%20Docker%20on%20an%20Ubuntu%20server">
Talk to Us Now
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/Calling a Python script from Node-REDGuide on how to execute Python scripts from Node-RED2024-07-10T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="why-use-python-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#why-use-python-with-node-red"></a> Why use python with Node-RED</h2>
<p>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.</p>
<p>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.</p>
<h2 id="installing-python" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#installing-python"></a> Installing Python</h2>
<p>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 <a href="https://wiki.python.org/moin/BeginnersGuide/Download">official guide</a> for instructions.</p>
<p>To verify if Python is installed, open your terminal and execute:</p>
<div style="position: relative" id="code-container-22">
<pre class="language-bash"><code id="code-22" class="language-bash">python <span class="token parameter variable">--version</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-22" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/calling-python-script-from-node-red-py-conformation-AFPy5JxSzq-547.avif 547w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/calling-python-script-from-node-red-py-conformation-AFPy5JxSzq-547.webp 547w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of terminal showing the python version installed, conforming it is installed" alt=""Screenshot of terminal showing the python version installed, conforming it is installed"" loading="lazy" decoding="async" src="https://flowfuse.com/img/calling-python-script-from-node-red-py-conformation-AFPy5JxSzq-547.jpeg" width="547" height="149" /></picture></p>
<p>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:</p>
<div style="position: relative" id="code-container-29">
<pre class="language-bash"><code id="code-29" class="language-bash">python3 <span class="token parameter variable">--version</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-29" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The specific command to use depends on how Python was installed and configured on your system. However, make sure to use <code>python <filename>.py</code> if the first command works, or <code>python3 <filename>.py</code> if the second command works, while executing Python scripts.</p>
<h2 id="executing-python-script-from-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#executing-python-script-from-node-red"></a> Executing Python Script from Node-RED</h2>
<p>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:</p>
<div id="nr-flow-139" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow139 = "\n[{\"id\":\"b9d7d6aff0016631\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[]},{\"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 < 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 __name__ == \\\"__main__\\\":\\n main()\\n\",\"output\":\"str\",\"x\":400,\"y\":100,\"wires\":[[\"d2d1450deaa588f4\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow139.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-139') })</script>
<p>Now, let's execute this Python script from Node-RED. To do that, we will use Node-RED's <a href="https://flowfuse.com/node-red/core-nodes/exec/">Exec</a> node, which allows running commands on your system.</p>
<ol>
<li>Drag an Inject node onto the canvas.</li>
<li>Drag an Exec node onto the canvas and Configure the command to <code>python ./example.py -u</code>. The -u flag prevents potential output buffering issues when executing Python scripts via exec.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/calling-python-scrpt-from-node-red-exec-node-QRFtgc9p8c-584.avif 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/calling-python-scrpt-from-node-red-exec-node-QRFtgc9p8c-584.webp 584w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Exec node executing python file" alt=""Screenshot of the Exec node executing python file"" loading="lazy" decoding="async" src="https://flowfuse.com/img/calling-python-scrpt-from-node-red-exec-node-QRFtgc9p8c-584.jpeg" width="584" height="774" /></picture></p>
<ol start="3">
<li>Drag a Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<div id="nr-flow-140" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow140 = "\n[{\"id\":\"2e26b84c0ce17312\",\"type\":\"exec\",\"z\":\"FFF0000000000001\",\"command\":\"python ./example.py -u\",\"addpay\":\"\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":460,\"y\":300,\"wires\":[[\"89589c56117004e0\"],[\"7fdb901b144749c2\"],[\"3f49b49308941782\"]]},{\"id\":\"739e08c1ec77c2a1\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[]},{\"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\":[]},{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow140.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-140') })</script>
<p>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 <code>{ code: 0 }</code>, which indicates your script has been successfully executed.</p>
<h2 id="reading-temperature-sensor-using-python-script" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#reading-temperature-sensor-using-python-script"></a> Reading Temperature Sensor using Python script</h2>
<p>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.</p>
<p>Before proceeding, ensure that Node-RED is running on a device connected to a temperature sensor. For detailed instructions, refer to <a href="https://flowfuse.com/node-red/hardware/">Setting Up Node-RED on Different Hardware</a>, In this case, we are running Node-RED on a Raspberry Pi 5 with a DHT11 sensor connected to it.</p>
<ol>
<li>Drag an Inject node onto the canvas, and set repeat to 1 seconds of interval.</li>
<li>Drag an Exec node and set the path to <code>python <filename>.py</code>, 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.</li>
<li>Drag the JSON node onto the canvas and set the action to "Always convert to JSON object".</li>
<li>Drag the Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/calling-python-scrpt-from-node-red-output-KWeN0VZRNz-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the Node-RED flow executing the python script that reads the sensor data" alt=""Image showing the Node-RED flow executing the python script that reads the sensor data"" loading="lazy" decoding="async" src="https://flowfuse.com/img/calling-python-scrpt-from-node-red-output-KWeN0VZRNz-800.webp" width="800" height="450" /></picture></p>
<p>Note: The Python script uses the <a href="https://docs.circuitpython.org/projects/dht/en/latest/index.html">adafruit-circuitpython</a> 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.</p>
<div id="nr-flow-141" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow141 = "\n[{\"id\":\"94bc6fa766c4b397\",\"type\":\"file\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"filename\":\"~/sensor.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[0])\\n except Exception as error:\\n dhtDevice.exit()\\n raise error\\n finally:\\n dhtDevice.exit()\\n\\ndef run():\\n publish()\\n\\nif __name__ == '__main__':\\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\":[]},{\"id\":\"88144cbc887aada9\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[],\"repeat\":\"1\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":660,\"wires\":[[\"c896267214914641\"]]},{\"id\":\"1b1d792011ffac2c\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"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 ~/sensor.py -u\",\"addpay\":\"payload\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"5\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":400,\"y\":660,\"wires\":[[\"1d02a33a018f0f8d\"],[],[]]},{\"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\":[]},{\"id\":\"343a9f704951f3ed\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Create python file that reads the sensor data\",\"info\":\"\",\"x\":510,\"y\":440,\"wires\":[]},{\"id\":\"216eb1333b2c264c\",\"type\":\"comment\",\"z\":\"FFF0000000000001\",\"name\":\"Execute the python file that read the data\",\"info\":\"\",\"x\":500,\"y\":580,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow141.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-141') })</script>
<h2 id="executing-python-script-with-arguments-from-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#executing-python-script-with-arguments-from-node-red"></a> Executing Python Script with Arguments from Node-RED</h2>
<p>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.</p>
<div id="nr-flow-142" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow142 = "\n[{\"id\":\"b9d7d6aff0016631\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":\"./example.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\":[]},{\"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 <number>\\\")\\n return\\n \\n user_input = sys.argv[1]\\n \\n # Check if the input is numeric\\n if user_input.isdigit() or (user_input[0] == '-' and user_input[1:].isdigit()):\\n number = int(user_input)\\n \\n # Conditionally render based on the input value\\n if number < 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 __name__ == \\\"__main__\\\":\\n main()\\n\",\"output\":\"str\",\"x\":460,\"y\":440,\"wires\":[[\"d2d1450deaa588f4\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow142.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-142') })</script>
<ol>
<li>Drag the Inject node onto the canvas.</li>
<li>Drag the Exec node onto the canvas, set the command to <code>python -u ./example.py <arg></code>, and replace the <code><arg></code> with your argument.</li>
<li>Now Drag the Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<p>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 <code>python ./example.py -30</code>. By accessing <code>sys.argv[1]</code>, we retrieve the argument -30. The index 1 is used because <code>sys.argv[0]</code> 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.</p>
<div id="nr-flow-143" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow143 = "\n[{\"id\":\"2e26b84c0ce17312\",\"type\":\"exec\",\"z\":\"FFF0000000000001\",\"command\":\"python -u ./example.py -30\",\"addpay\":\"\",\"append\":\"\",\"useSpawn\":\"false\",\"timer\":\"\",\"winHide\":false,\"oldrc\":false,\"name\":\"\",\"x\":520,\"y\":580,\"wires\":[[\"89589c56117004e0\"],[\"7fdb901b144749c2\"],[\"3f49b49308941782\"]]},{\"id\":\"739e08c1ec77c2a1\",\"type\":\"inject\",\"z\":\"FFF0000000000001\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"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\":[]},{\"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\":[]},{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow143.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-143') })</script>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/calling-python-script-from-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Simplify Node-RED management and deployment with FlowFuse. Optimize, scale, secure, collaborate effortlessly, and access your instance remotely.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Calling%20a%20Python%20script%20from%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/FlowFuse 2.6: AI Infused Node-RED, Persistent File Storage & Lots MoreLowering the barrier to entry for new users, and enhancing the flexibility and functionality of the platform.2024-07-04T00:00:00ZJoe Pavitt<p>FlowFuse 2.6 is packed with great new features, and in this release we've had a heavy focus on improving the development experience of Node-RED, lowering the barrier to entry for new users and aligning to our <a href="https://flowfuse.com/handbook/product/strategy/#simplified-hosting">Simplified Hosting</a> and <a href="https://flowfuse.com/handbook/product/strategy/#low-code">Low-Code</a> plans from our <a href="https://flowfuse.com/handbook/product/strategy/">Product Strategy</a>.</p>
<!--more-->
<h2 id="improving-the-node-red-experience" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#improving-the-node-red-experience"></a> Improving the Node-RED Experience</h2>
<p>Whilst a big part of FlowFuse is the ability to run Node-RED, we're also focussing on how to improve the experience of <em>building</em> with Node-RED on FlowFuse too.</p>
<h3 id="ai-infused-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#ai-infused-node-red"></a> AI-Infused Node-RED</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/assistant-dialog-function-node-builder-Vs3rbpLCAB-519.avif 519w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/assistant-dialog-function-node-builder-Vs3rbpLCAB-519.webp 519w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "FlowFuse Assistant" dialog box" loading="lazy" decoding="async" src="https://flowfuse.com/img/assistant-dialog-function-node-builder-Vs3rbpLCAB-519.jpeg" width="519" height="361" /></picture>
<em>Screenshot of an example instruction sent to FlowFuse Assistant.</em></p>
<p>One of the joys of Node-RED is how it empowers users not experienced with development to create bespoke applications. A popular and very powerful node in Node-RED is the "function" node. This node allows users to write JavaScript code to manipulate messages. However, this can be daunting for users who are not familiar with JavaScript, it's a steep learning curve. As such, when you run Node-RED in FlowFuse, you'll be able to use the <strong>Node-RED Assistant</strong> in both the Editor toolbar, and the function nodes:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/assistant-toolbar-gf0OC1fTGW-496.avif 496w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/assistant-toolbar-gf0OC1fTGW-496.webp 496w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "FlowFuse Assistant" button available in the Editor Toolbar" loading="lazy" decoding="async" src="https://flowfuse.com/img/assistant-toolbar-gf0OC1fTGW-496.jpeg" width="496" height="234" /></picture>
<em>Screenshot showing the "FlowFuse Assistant" button available in the Editor Toolbar</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/assistant-function-node-inline-code-lens-uOYcqOP95s-648.avif 648w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/assistant-function-node-inline-code-lens-uOYcqOP95s-648.webp 648w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "Ask the FlowFuse Assistant" button available in the function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/assistant-function-node-inline-code-lens-uOYcqOP95s-648.jpeg" width="648" height="219" /></picture>
<em>Screenshot showing the "Ask the FlowFuse Assistant" button available in the function node</em></p>
<p>The Node-RED Assistant is an AI-powered tool that can help you write JavaScript code in the function node. It can suggest code snippets, help you debug your code, and even write code for you. This is a game-changer for users who are inexperienced with JavaScript, as it lowers the barrier to entry and makes it easier to create applications.</p>
<h3 id="immersive-editor-experience" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#immersive-editor-experience"></a> Immersive Editor Experience</h3>
<p>During our investigation on how FlowFuse and Node-RED are used together, we found out that many users have to frequently switch between the Node-RED Editor and FlowFuse UI. This back-and-forth movement was often necessary to view logs, save snapshots, restart the Editor after updates, and perform other tasks.</p>
<p>We're always seeking to reduce friction in the FlowFuse user experience, and as such, we've introduced a large overhaul of the developer experience for Node-RED when running in FlowFuse, in what we're calling the "Immersive Editor".</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/immersive-editor-8UU5N-h1t2-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/immersive-editor-8UU5N-h1t2-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "Immersive Editor" view" loading="lazy" decoding="async" src="https://flowfuse.com/img/immersive-editor-8UU5N-h1t2-1920.jpeg" width="1920" height="1102" /></picture>
<em>Screenshot showing the "Immersive Editor" view, where the FlowFuse navigation is available as a floating bar.</em></p>
<p>Now, the instances tabs are all available in the <em>same</em> view as the Editor.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/immersive-editor-collapsed-s3htl2SV2K-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/immersive-editor-collapsed-s3htl2SV2K-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "Collapsed" FlowFuse bar" loading="lazy" decoding="async" src="https://flowfuse.com/img/immersive-editor-collapsed-s3htl2SV2K-1920.jpeg" width="1920" height="177" /></picture>
<em>Screenshot showing the "FlowFuse" button at the bottom of the Node-RED Editor.</em></p>
<p>Don't worry though, if you want the full editor experience again, you can just collapse the FlowFuse menus down to a little "FlowFuse" button at the bottom.</p>
<p>The new Immersive Editor is available for instances running Node-RED 4.0.2 or later - older versions of Node-RED will still use the separate views.</p>
<h2 id="persistent-file-storage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#persistent-file-storage"></a> Persistent File Storage</h2>
<p>Since the early days of FlowFuse, we have provided custom File nodes that can be used to read and write individual files from a flow. This was necessary because the local file system was not considered persistent; restarting an instance would reset the file system back to how it was when the instance first started.</p>
<p>Whilst this solved the immediate problem for the File nodes, we know there were 3rd party nodes that would want to use the file system as well - and we couldn't expect them to update to work with our custom solution.</p>
<p>With the 2.6 release, each instance now gets a piece of persistent file system they can read and write to normally, from any node - with full confidence those files will be persisted between restarts.</p>
<p>This unlocks lots of new capabilities using nodes from the community. For example, the <a href="https://flows.nodered.org/node/node-red-node-sqlite">SQLite</a> nodes can be used to quickly add a locally managed database to store your data in.</p>
<p>All newly created instances of FlowFuse Cloud from today will have this storage enabled. If you have an existing instance you'd like to move over, then do get <a href="https://flowfuse.com/contact-us/">in touch</a> and we can help move you over.</p>
<h2 id="other-highlights" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#other-highlights"></a> Other Highlights</h2>
<h3 id="compact-applications-view" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#compact-applications-view"></a> Compact Applications View</h3>
<p>This is a great example of collaboration with our customers. Three weeks ago, a customer reached out to us with a design proposal for the main "Applications" view. Within three weeks it's not only been implemented, but is now live, running in FlowFuse Cloud, and available in FlowFuse 2.6.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-applications-view-UhFrXncT2D-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-applications-view-UhFrXncT2D-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of new "Applications" view for a given Team in FlowFuse." loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-applications-view-UhFrXncT2D-1920.jpeg" width="1920" height="1103" /></picture>
<em>Screenshot of new "Applications" view for a given Team in FlowFuse.</em></p>
<p>The improvement here is that we've moved instances and devices to be shown as a maximum of three per row (rather than the one perviously) meaning you can see far more content at a glance, and hopefully, get to where you need to go in fewer clicks.</p>
<h3 id="import%2Fexport-blueprints" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#import%2Fexport-blueprints"></a> Import/Export Blueprints</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/blueprint-import-export-5bXUzp42D9-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/blueprint-import-export-5bXUzp42D9-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the admin page for "Flow Blueprints" with the new "Import"/"Export" buttons" loading="lazy" decoding="async" src="https://flowfuse.com/img/blueprint-import-export-5bXUzp42D9-1920.jpeg" width="1920" height="1101" /></picture>
<em>Screenshot showing the admin page for "Flow Blueprints" with the new "Import"/"Export" buttons</em></p>
<p>Administrators of FlowFuse instances can now import and export Blueprints. This is the first stage of a larger feature set for Blueprints that will make it easier to share common flows and patterns across FlowFuse instances, and see many extensions to our <a href="https://flowfuse.com/blueprints/">Blueprint Library</a>.</p>
<h3 id="multi-line-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#multi-line-environment-variables"></a> Multi-line Environment Variables</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multiline-env-vars-3Eeh7XAYRk-843.avif 843w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multiline-env-vars-3Eeh7XAYRk-843.webp 843w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the "Environment Variables" table in the Instance's Settings" loading="lazy" decoding="async" src="https://flowfuse.com/img/multiline-env-vars-3Eeh7XAYRk-843.jpeg" width="843" height="434" /></picture>
<em>Screenshot showing the "Environment Variables" table in an Instance's Settings</em></p>
<p>We now support the use of multi-line environment variables for your Node-RED instances running on FlowFuse. This unlocks the ability to store certs or multi-line values like JSON as environment variables.</p>
<p>We've also improved the <code>.env</code> importing to support the use of multi-line values too.</p>
<h3 id="and-much-more..." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#and-much-more..."></a> And Much More...</h3>
<p>For a full list of everything that went into our 2.6 release, you can check out the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.6.0">release notes</a>.</p>
<p>We're always working to enhance your experience with FlowFuse. We're always interested in your thoughts about FlowFuse too. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</p>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#try-it-out"></a> Try it out</h2>
<h3 id="self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#self-hosted"></a> Self-Hosted</h3>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud: <a href="https://app.flowfuse.com/account/create">Get started for free</a> now, and you'll have your own Node-RED instances running in the Cloud within minutes.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p>If you're using <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>, then there is nothing you need to do - it's already running 2.6, and you may have already been playing with the new features.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/flowfuse-2-6-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/Node-RED Dashboard Formally DeprecatedIt 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.2024-06-27T00:00:00ZJoe Pavitt<p>Dave Conway-Jones, the lead maintainer of Node-RED Dashboard, has <a href="https://discourse.nodered.org/t/node-red-dashboard-v1-deprecation-notice/89006">just announced</a> that Node-RED Dashboard has been formally deprecated, meaning there will be no further development activity on the project.</p>
<p><a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard</a> (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.</p>
<!--more-->
<h2 id="what's-new-in-dashboard-2.0%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#what's-new-in-dashboard-2.0%3F"></a> What's new in Dashboard 2.0?</h2>
<p>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:</p>
<ul>
<li><strong>Multi Tenancy:</strong> 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.</li>
<li><strong>Responsive Layouts:</strong> 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.</li>
<li><strong>New Nodes:</strong> We've introduced a five new nodes; <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-button-group.html">Button Group</a>, <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-radio-group.html">Radio Group</a>, <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-file-input.html">File Upload</a>, <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-event.html">Event</a> and <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-table.html">Table</a> to help you build your Dashboards, without needing to write custom code, or depend on third-party libraries.</li>
<li><strong>Directly Install on Mobile:</strong> 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.</li>
<li><strong>Vuetify Framework:</strong> FlowFuse Dashboard is built on VueJS, and with that, we made the decision to integrate in <a href="https://vuetifyjs.com/en/components/all/#containment">Vuetify</a>, 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 <code>ui-template</code> node.</li>
</ul>
<h2 id="what-we-have-planned" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#what-we-have-planned"></a> What we have planned</h2>
<p>We are actively investing into the development of FlowFuse Dashboard, and releasing new features and updates on a regular basis. We have a public <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Project Management Board</a> that show exactly what we're working on, and what we have planned in the near future. Some particular highlights include:</p>
<ul>
<li><strong>Visual Layout Editor:</strong> 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 <a href="https://github.com/FlowFuse/node-red-dashboard/issues/30">here</a>.</li>
<li><strong>Diversity of Data Visualisation:</strong> We are planning to provide a richer ecosystem of data visualisations. We're also likely to be switching to <a href="https://github.com/FlowFuse/node-red-dashboard/issues/782">Apache eCharts</a> (subject to further investigation), and are considering a <code>ui-template</code>-type node that can be used to build <a href="https://github.com/FlowFuse/node-red-dashboard/issues/58">custom charts</a> too.</li>
<li><strong>Feature Parity:</strong> 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 <a href="https://github.com/FlowFuse/node-red-dashboard/issues/555">programmatic (mustache) title and labels</a>.</li>
</ul>
<p>If there are any other features you'd like to see, please do open a <a href="https://github.com/FlowFuse/node-red-dashboard/issues/new/choose">Feature Request</a>, and we'll do our best to accommodate.</p>
<h2 id="getting-started-with-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#getting-started-with-flowfuse-dashboard"></a> Getting Started with FlowFuse Dashboard</h2>
<p>If you're ready to get started with FlowFuse Dashboard, you can install it directly from the Node-RED Palette Manager. Simply search for <code>@flowfuse/node-red-dashboard</code> and install the latest version.</p>
<p><img data-zoomable="" alt="A short recording to show how easy it is to setup your first dashboard" loading="lazy" decoding="async" src="https://dashboard.flowfuse.com/assets/getting-started.DHDsIsZl.gif" width="undefined" height="undefined" />
<em>A short recording to show how easy it is to setup your first dashboard</em></p>
<p>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.</p>
<h3 id="resources" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#resources"></a> Resources</h3>
<p>We have a comprehensive <a href="https://dashboard.flowfuse.com/getting-started.html">Getting Started Guide</a> that will walk you through the process of setting up your first Dashboard. You can download our <a href="https://dashboard.flowfuse.com/#download-our-e-book">"Ultimate Guide" eBook</a> too.</p>
<h3 id="dashboard-workshop" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#dashboard-workshop"></a> Dashboard Workshop</h3>
<p>We'll running a <a href="https://flowfuse.com/webinars/2024/workshop-dashboard/">Workshop</a> 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.</p>
<p>The recording will also be available on the <a href="https://www.youtube.com/@FlowFuseInc">FlowFuse YouTube channel</a> after the event.</p>
<h2 id="migrating-to-flowfuse-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#migrating-to-flowfuse-dashboard"></a> Migrating to FlowFuse Dashboard</h2>
<p>If you're looking to migrate your existing Node-RED Dashboard flows to FlowFuse Dashboard, we have a <a href="https://flowfuse.com/product/dashboard/#migration-service">Migration Guide</a> that details all of the features and properties that have, and haven't, been migrated into FlowFuse Dashboard.</p>
<p>It's worth noting that FlowFuse Dashboard <em>will</em> 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.</p>
<p>This week, we also made available the first iteration of the <a href="https://flowfuse.com/product/dashboard/#migration-service">Dashboard Migration Service</a>:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-migration-service-fg-UETq46S-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-migration-service-fg-UETq46S-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the Dashboard Migration Service available on FlowFuse's website" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-migration-service-fg-UETq46S-1920.jpeg" width="1920" height="884" /></picture>
<em>Screenshot showing the Dashboard Migration Service available on FlowFuse's website</em></p>
<p>Currently, it'll convert your <code>ui_tab</code>, <code>ui_group</code> nodes, and setup your new <code>ui-theme</code> and <code>ui-base</code> nodes for you. We'll be extending this over time, and eventually hope to auto-convert most of your existing Dashboard 1.0 flows.</p>
<h2 id="final-thoughts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-1-deprecated/#final-thoughts"></a> Final Thoughts</h2>
<p>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 <a href="https://discourse.nodered.org/tag/dashboard-2">Node-RED Community Forum</a>.</p>
<p>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 <a href="https://dashboard.flowfuse.com/contributing/">here</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/Interacting with Google Sheets from Node-REDGuide to learn how to write, read, update and delete data in Google sheet using Node-RED.2024-06-21T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="what-is-the-google-sheet%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#what-is-the-google-sheet%3F"></a> What is the Google Sheet?</h2>
<p><a href="https://www.google.com/sheets/about/">Google Sheets</a> 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.</p>
<h2 id="prequisite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#prequisite"></a> Prequisite</h2>
<p>Before proceeding, make sure you have installed the following node from the pallet manager.</p>
<ul>
<li><a href="https://flows.nodered.org/node/node-red-contrib-google-sheets">node-red-contrib-google-sheets</a></li>
</ul>
<h2 id="interacting-with-google-sheets-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#interacting-with-google-sheets-with-node-red"></a> Interacting with Google Sheets with Node-RED</h2>
<p>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.</p>
<ol>
<li>Open your browser and go to <a href="https://console.cloud.google.com/projectselector2/apis/library/sheets?supportedpurview=project&authuser=0">Service accounts</a>.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_1-2Tnoi5sWGt-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_1-2Tnoi5sWGt-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'CREATE PROJECT' button" alt="Screenshot showing the 'CREATE PROJECT' button" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_1-2Tnoi5sWGt-1851.jpeg" width="1851" height="858" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_2-5h4_FxP_Um-730.avif 730w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_2-5h4_FxP_Um-730.webp 730w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the Form to create the project" alt=""Screenshot showing the Form to create the project"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_2-5h4_FxP_Um-730.jpeg" width="730" height="800" /></picture></p>
<ol start="3">
<li>Go to the main menu by clicking the menu icon in the top left, then select "APIs & Services."</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_3-DB8WDk7RPo-1853.avif 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_3-DB8WDk7RPo-1853.webp 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'APIs & Services' option from the main menu" alt=""Screenshot showing the 'APIs & Services' from the menu"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_3-DB8WDk7RPo-1853.jpeg" width="1853" height="842" /></picture></p>
<ol start="4">
<li>Click on "Enable APIs and Services" in the header.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_4-UAaWI2hW_Q-1835.avif 1835w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_4-UAaWI2hW_Q-1835.webp 1835w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Enable APIs and Services' option" alt=""Screenshot showing the 'Enable APIs and Services' option" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_4-UAaWI2hW_Q-1835.jpeg" width="1835" height="847" /></picture></p>
<ol start="5">
<li>In the search bar, type "Google Sheets" and select it from the results.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_5-qpMRYDAaou-1845.avif 1845w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_5-qpMRYDAaou-1845.webp 1845w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the Google Sheet in the search result" alt=""Screenshot showing the Google Sheet in the search result" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_5-qpMRYDAaou-1845.jpeg" width="1845" height="841" /></picture></p>
<ol start="6">
<li>Click the "Enable" button to enable the Google Sheets API.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_6-OOpOFFRgo1-727.avif 727w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_6-OOpOFFRgo1-727.webp 727w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Enable' button" alt=""Screenshot showing the 'Enable' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_6-OOpOFFRgo1-727.jpeg" width="727" height="298" /></picture></p>
<ol start="7">
<li>Go back to the main menu and click on "IAM & Admin," then select "Service Accounts" from the left sidebar.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_7-BPQJokof21-1838.avif 1838w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_7-BPQJokof21-1838.webp 1838w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'IAM & Admin' from the menu" alt=""Screenshot showing the 'IAM & Admin' from the menu"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_7-BPQJokof21-1838.jpeg" width="1838" height="811" /></picture></p>
<ol start="8">
<li>Click on "Create Service Account" in the header. Enter the necessary details and click "Create" to proceed.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_8-NYG2RG889j-1841.avif 1841w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_8-NYG2RG889j-1841.webp 1841w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Create Service Account' option" alt=""Screenshot showing the 'Create Service Account' option"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_8-NYG2RG889j-1841.jpeg" width="1841" height="858" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_9-4yzyu2QzTb-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_9-4yzyu2QzTb-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Create' button" alt=""Screenshot showing the 'Create' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_9-4yzyu2QzTb-1851.jpeg" width="1851" height="847" /></picture></p>
<ol start="9">
<li>Select the Role from the "Owner" and click on the "Continue" button.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_10-sFQbozs6Nu-1846.avif 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_10-sFQbozs6Nu-1846.webp 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Continue' button" alt=""Screenshot showing the 'Continue' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_10-sFQbozs6Nu-1846.jpeg" width="1846" height="851" /></picture></p>
<ol start="10">
<li>Click "Done." Make sure to copy the generated service account email and save it for later use.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_11-nWSB_WLe3d-1849.avif 1849w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_11-nWSB_WLe3d-1849.webp 1849w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Done' button" alt=""Screenshot showing the 'Done' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_11-nWSB_WLe3d-1849.jpeg" width="1849" height="848" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_14-KZa_Bst29B-1856.avif 1856w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_14-KZa_Bst29B-1856.webp 1856w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the created service account email" alt=""Screenshot showing the created service account email"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_14-KZa_Bst29B-1856.jpeg" width="1856" height="846" /></picture></p>
<ol start="11">
<li>To generate a private key, click on the three dots icon on the right of the newly created service account and select "Manage keys."</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.avif 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.webp 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the three dot icon" alt=""Screenshot showing the three dot icon"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.jpeg" width="1853" height="860" /></picture></p>
<ol start="12">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.avif 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.webp 1853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'Add key' and the 'Create new key'" alt=""Screenshot showing the 'Add key' and the 'Create new key'"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_15-H9KgnIyuEx-1853.jpeg" width="1853" height="860" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_13-q9YET4YZ4Q-1844.avif 1844w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_13-q9YET4YZ4Q-1844.webp 1844w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the 'JSON' option and 'Create new key' button" alt=""Screenshot showing the 'JSON' option and 'Create new key' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-page_13-q9YET4YZ4Q-1844.jpeg" width="1844" height="846" /></picture></p>
<h3 id="configuring-the-google-sheet-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#configuring-the-google-sheet-node"></a> Configuring the Google Sheet Node</h3>
<p>Before proceeding, ensure you have added the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">environment variable</a> 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.</p>
<ol>
<li>Drag a GSheet node onto the canvas.</li>
<li>Double-click on the node and click on the pencil icon next to "creds."</li>
<li>Enter the environment variable added for the private key in the "creds" field and click "Add."</li>
<li>Go to the Google Sheet you want to interact with and copy its ID from the URL. The URL will be in this format: <code>https://docs.google.com/spreadsheets/d/<id_of_sheet>/</code></li>
<li>Return to your Node-RED instance, double-click on the GSheet node again, and paste the spreadsheet ID into the "SpreadsheetID" field.</li>
<li>Enter the range of cells you want to work with using the syntax <code><sheetname!first-cell-name:last-cell-name></code>. For example, use <code>Sheet1!A1:C3</code> 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 (<code>A1:A5</code>), a column (<code>A1:E1</code>), or a block (<code>A1:C3</code>) within the spreadsheet.</li>
</ol>
<h3 id="writing-data-to-cells" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#writing-data-to-cells"></a> Writing Data to Cells</h3>
<p>For demonstration purposes, I will write simulated sensor data which includes a timestamp and sensor data.</p>
<ol>
<li>Drag the Inject node onto the canvas, and set <code>msg.payload</code> to <code>[$moment().format(), $random() * 100]</code> as a JSONata expression, and set it to repeat every 3 seconds of interval.</li>
<li>Double-click on the GSheet node, select the method to "Append Row" set the range to <code><sheetname>!A2</code>, and replace <code>sheetname</code> with the name of your sheet. I have defined cell A2 because I want to start writing data from cell A2.</li>
<li>Drag the Debug node onto the canvas, which will help in debugging in case of any error.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-write-VoKXhyWfF1-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the write operation" alt=""Image showing the write operation"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-write-VoKXhyWfF1-800.webp" width="800" height="450" /></picture></p>
<p>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.</p>
<h3 id="reading-data-from-cells" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#reading-data-from-cells"></a> Reading Data from Cells</h3>
<ol>
<li>Drag an Inject node onto the canvas.</li>
<li>Drag another GSheet node onto the canvas, and set the method to "Get Cells" and the range to <code><sheetname>!A2:C1000</code>, as I wanted to read data from cell A2 to the next 1000 cells.</li>
<li>Drag a Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-read-H_8E9DPH87-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the read operation" alt=""Image showing the read operation"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-read-H_8E9DPH87-800.webp" width="800" height="450" /></picture></p>
<h3 id="updating-data-of-cells" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#updating-data-of-cells"></a> Updating Data of Cells</h3>
<ol>
<li>Drag an Inject node onto the canvas, and set the updated value as the <code>msg.payload</code>.</li>
<li>Drag another GSheet node onto the canvas, and set the method to "Update Cells" and the range to <code><sheetname>!A2</code>, as I wanted to update the value of cell A2.</li>
<li>Drag a Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-update-50f_dQqz0E-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the update operation" alt=""Image showing the update operation"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-update-50f_dQqz0E-800.webp" width="800" height="450" /></picture></p>
<h3 id="deleting-data-from-cells" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#deleting-data-from-cells"></a> Deleting Data from Cells</h3>
<ol>
<li>Drag an Inject node onto the canvas.</li>
<li>Drag another GSheet node onto the canvas, and set the method to "Clear Cells" and the range to <code><sheetname>!A2:C50</code>, as I wanted to clear the first 50 records.</li>
<li>Drag a Debug node onto the canvas.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-EGeUvaOv88-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing the delete operation" alt=""Image showing the delete operation"" loading="lazy" decoding="async" src="https://flowfuse.com/img/interacting-with-google-sheet-from-node-red-delete-EGeUvaOv88-800.webp" width="800" height="450" /></picture></p>
<p>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.</p>
<div id="nr-flow-138" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow138 = "\n[{\"id\":\"7d0282761979574c\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Wrting data to the cells\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[\\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\":[]},{\"id\":\"2c916b1d5c10dffe\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Read the cells data\",\"props\":[],\"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\":[]},{\"id\":\"c20997333f9d4bda\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Updating the cells data\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[\\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\":[]},{\"id\":\"c9ceec9844fa74a9\",\"type\":\"inject\",\"z\":\"baa50b8a4762ec1f\",\"name\":\"Deleting the cells data\",\"props\":[],\"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\":[]},{\"id\":\"d38cb80ae8574ea6\",\"type\":\"gauth\",\"name\":\"Unknown\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow138.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-138') })</script>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/interacting-with-google-sheet-from-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Interacting%20with%20Google%20Sheets%20from%20Node-RED">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/Multi-Tenancy available for everyone with FlowFuse's Dashboard 2.0With a recent update in Node-RED Dashboard 2.0, we've made some significant changes to the multi-tenancy feature set. Discover what's new and how it can benefit your projects.2024-06-21T00:00:00ZJoe Pavitt<p>FlowFuse Dashboard has featured multi-tenancy features through the FlowFuse User Addon. This made user based applications available only to specific FlowFuse team tiers and customers. However, the Node-RED community wanted to use the same feature set in cases FlowFuse didn't consider initially. Taking this feedback on board, today we announce some significant changes to how you can build Dashboards with multi-tenancy in mind.</p>
<p>Having taken that feedback on board, we've made some significant changes to how you can build Dashboards with multi-tenancy in mind.</p>
<!--more-->
<p>A quick summary of the changes are as follows:</p>
<ul>
<li><strong>Socket constraints moved to core Dashboard:</strong> The ability to constrain communications to a specific client (using the <code>msg._client.socketId</code>) has been moved to the core of Dashboard 2.0, and is no longer a feature of the FlowFuse User Addon. As such, it's available to <em>all</em> users, for <em>all</em> node types.</li>
<li><strong>FlowFuse User Addon now Source Available:</strong> The addon is available to install in <em>any</em> instances of Node-RED running on FlowFuse, so that's all team tiers on FlowFuse Cloud, and any self-hosted instances of FlowFuse too. It is available to install from Node-RED's Palette Manager.</li>
<li><strong>Cloudflare Auth Plugin:</strong> Release of the first community-created Auth Plugin for Dashboard for adding a <code>user</code> object when authenticating with Cloudflare. (See more details and install the plugin <a href="https://flows.nodered.org/node/@fullmetal-fred/node-red-dashboard-2-cloudflare-auth">here</a>)</li>
</ul>
<h2 id="using-client-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#using-client-data"></a> Using Client Data</h2>
<p>Available since our <code>1.10.0</code> release of Dashboard, we have a new sidebar tab - "Client Data".</p>
<p>This new tab acts as a portal to control whether data about the connected clients is included in any events emitted in the Node-RED editor (appended into <code>msg._client</code>). You can then use the "Accept CLient Data" options to define which nodes accept that data as a constraint for communication.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-multi-tenancy-sidebar-UxaLrNDaNR-1540.avif 1540w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-multi-tenancy-sidebar-UxaLrNDaNR-1540.webp 1540w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the new "Client Data" sidebar available with Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-multi-tenancy-sidebar-UxaLrNDaNR-1540.jpeg" width="1540" height="1888" /></picture>
<em>Screenshot showing the new "Client Data" sidebar available with Dashboard</em></p>
<p>In this case, if we send a <code>msg</code> to any <code>ui-notification</code> or <code>ui-control</code> which a specified <code>msg._client.socketId</code>, then that <code>msg</code> will <em>only</em> be sent to the relevant socket connection.</p>
<h2 id="building-multi-tenant-dashboards" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#building-multi-tenant-dashboards"></a> Building Multi-Tenant Dashboards</h2>
<p>We have introduced a new section to our documentation on <a href="https://dashboard.flowfuse.com/getting-started.html#design-patterns">Design Patterns</a>.</p>
<p>To summarise briefly, we now consider there to be two primary design patterns when building with Dashboard:</p>
<ul>
<li><strong>Single Source of Truth</strong>: All users of your Dashboard will see the same data. This is useful for industrial IoT or Home Automation applications.</li>
<li><strong>Multi-Tenancy</strong>: Data shown in a particular widget is unique to a given client/session/user. This represents a more traditional web application, where each user has their own session and associated data.</li>
</ul>
<p>Note that these patterns can be intertwined, some widgets on a screen may be driven by "Single Source of Truth", and others by "Multi-Tenancy".</p>
<h4 id="building-a-single-source-of-truth-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#building-a-single-source-of-truth-dashboard"></a> Building a Single Source of Truth Dashboard</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/design-pattern-single-X7woI7cnD4-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/design-pattern-single-X7woI7cnD4-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example flow diagram to show the flow of data in a "Single Source of Truth" architecture" loading="lazy" decoding="async" src="https://flowfuse.com/img/design-pattern-single-X7woI7cnD4-1920.jpeg" width="1920" height="511" /></picture>
<em>Example flow diagram to show the flow of data in a "Single Source of Truth" architecture</em></p>
<p>Data can be sent to these widgets at any time, when a user connects to the Dashboard, the respective widget will load the relevant data from the centralised data source in Node-RED and show it to the user.</p>
<h4 id="building-a-multi-tenancy-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#building-a-multi-tenancy-dashboard"></a> Building a Multi-Tenancy Dashboard</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/design-pattern-client-lw4PKpXfti-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/design-pattern-client-lw4PKpXfti-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example flow diagram to show the flow of data in a "Multi-Tenancy" architecture" loading="lazy" decoding="async" src="https://flowfuse.com/img/design-pattern-client-lw4PKpXfti-1920.jpeg" width="1920" height="544" /></picture>
<em>Example flow diagram to show the flow of data in a "Multi-Tenancy" architecture</em></p>
<p>In this pattern, a very useful node is the <code>ui-event</code> node which fires a <code>msg</code> when a user views a page. This <code>msg</code> will contain a <code>msg._client</code> object, detailing the client's connection.</p>
<p>This <code>_client</code> object contains the <code>socketId</code> of the user (and potentially more depending on any <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#authentication-plugins">Authentication plugins</a> used). This <code>msg</code> can then be passed through to any other widget, and if configured to "Accept Client Constraints" that <code>msg</code> will only be sent to the specified client.</p>
<h2 id="authentication-plugins" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#authentication-plugins"></a> Authentication Plugins</h2>
<p>In this release we've also added a special category of plugins, "Authentication Plugins".</p>
<p>These plugins register themselves with Dashboard, and are permitted to add to the <code>msg._client</code> object. This can be useful for adding additional information about end users, such as their user ID, e-mail address or username.</p>
<p>This data can be used to constrain communications from Node-RED to a specific <em>user</em> rather than just a <em>socket connection</em>, which is far more reliable and secure.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-include-client-data-l0DhMt_d7S-1192.avif 1192w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-include-client-data-l0DhMt_d7S-1192.webp 1192w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing "FlowFuse User" being appended to `_client` by the FlowFuse User Addon" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-include-client-data-l0DhMt_d7S-1192.jpeg" width="1192" height="296" /></picture>
<em>Screenshot showing "FlowFuse User" being appended to <code>_client</code> by the FlowFuse User Addon</em></p>
<p>Any active plugins you have installed, will also be detailed in the new "Client Data" sidebar (detailed above) so you can see which plugins are active and what data they are adding to the <code>msg._client</code> object.</p>
<h3 id="flowfuse-user-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#flowfuse-user-addon"></a> FlowFuse User Addon</h3>
<p>Whilst the plugin was first published a few months back, after hearing community feedback, we've made changes and are now publishing the plugin to the Node_RED Palette Manager. As such, it's available to all users running FlowFuse.</p>
<p>The addon appends a <code>user</code> object to the <code>msg._client</code> object, populated with the details of the FlowFuse user performing the relevant actions in Dashboard. You can use this information to build multi-tenant Dashboards.</p>
<p>You can <a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard-2-user-addon">install the FlowFuse User Addon</a> from the Palette Manager in the Node-RED editor.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-instance-security-kaQ1bJqxHx-1327.avif 1327w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-instance-security-kaQ1bJqxHx-1327.webp 1327w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Instance settings in FlowFuse to enable "FlowFuse User Authentication"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-instance-security-kaQ1bJqxHx-1327.jpeg" width="1327" height="970" /></picture>
<em>Screenshot of the Instance settings in FlowFuse to enable "FlowFuse User Authentication"</em></p>
<p>It's worth noting that instances must have <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#enabling-flowfuse-user-authentication">"FlowFuse User Authentication"</a> switched on in the instance's settings.</p>
<h3 id="cloudfare-user-addon-(community-contribution)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#cloudfare-user-addon-(community-contribution)"></a> Cloudfare User Addon (Community Contribution)</h3>
<p>We're also thrilled to announce that the first community-contributed plugin has been <a href="https://flows.nodered.org/node/@fullmetal-fred/node-red-dashboard-2-cloudflare-auth">published</a> which will append a <code>user</code> object to the <code>msg._client</code> object when authenticating with Cloudflare.</p>
<p>A huge thanks to Fred Loucks (<a href="https://github.com/fullmetal-fred">@fullmetal-fred</a> on GitHub) for his great contribution!</p>
<h2 id="build-multi-tenant-dashboards-with-flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/dashboard-multi-tenancy/#build-multi-tenant-dashboards-with-flowfuse-cloud"></a> Build Multi Tenant Dashboards with FlowFuse Cloud</h2>
<p>Start by following our <a href="https://dashboard.flowfuse.com/user/multi-tenancy.html#building-multi-tenant-dashboards">Getting Started Guide</a>.</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Multi-Tenancy%20available%20for%20everyone%20with%20FlowFuse%27s%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/node-red-4-on-flowfuse-cloud/Node-RED 4: Bringing better collaboration to FlowFuse CloudMaking use of the Multiplayer Mode to collaborate with your teams2024-06-20T00:00:00ZNick O'Leary<p>Node-RED 4.0 is the new major release of the project, which is at the heart of all we do at FlowFuse. It brings a range of new features that continue to make Node-RED the first choice for low-code development.</p>
<!--more-->
<p>As <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/">we wrote recently</a>, our product mission statement is to provide the best way to build, manage and deploy Node-RED applications at scale, in reliable and secure production environments. This drives the work we do both within the FlowFuse platform and our contributions back into the Node-RED project.</p>
<p>The <a href="https://nodered.org/blog/2024/06/20/version-4-0-released">Node-RED 4.0 release</a> includes some of our recent contributions to improve the Build part of that mission - more specifically around the Collaborative Development part.</p>
<p>Within FlowFuse we already have features such as the <a href="https://flowfuse.com/docs/user/shared-library/">Team Library</a> that help team members share their flows with each other. That works well when working across separate Node-RED instances, but we wanted to improve the experience when working with the <em>same</em> Node-RED instance.</p>
<p>There has been a common complaint from users in the community on how Node-RED handles multiple users editing flows at the same time. Our goal is to make collaboration as simple and natural as possible and Node-RED 4.0 brings us a big step forward.</p>
<h2 id="multiplayer-mode" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/node-red-4-on-flowfuse-cloud/#multiplayer-mode"></a> Multiplayer Mode</h2>
<p>A key problem has been not knowing what other users were doing in the editor; not knowing that someone else was making changes until they deploy them, causing you to be interrupted in your own work to figure out how to pull their changes into your editor.</p>
<p>The new Node-RED Multiplayer Mode helps to address this by showing you who else has the editor open and 'where' they are in the editor - such as which tab they have open, or if they are currently editing a node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr4-multiplayer-lAUqeg0tPk-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr4-multiplayer-lAUqeg0tPk-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of Node-RED multiplayer in action" alt=""Screenshot of Node-RED multiplayer in action"" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr4-multiplayer-lAUqeg0tPk-650.jpeg" width="650" height="319" /></picture></p>
<p>This is just the first step to our eventual goal of being able to see the changes others are making in real-time.</p>
<h2 id="concurrent-deploy-handling" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/node-red-4-on-flowfuse-cloud/#concurrent-deploy-handling"></a> Concurrent Deploy Handling</h2>
<p>A second thread to this work has been to improve the workflow when someone deploys changes whilst you're busy working on your own flows.</p>
<p>With previous versions of Node-RED, each time someone deployed changes, you would receive a notification you couldn't ignore - having to deal it before you could continue what you were doing. Getting one notification wasn't so bad, but if your team mate was being particularly productive, they might be hitting the deploy button regularly - each time interrupting your own flow.</p>
<p>With Node-RED 4.0, we've made some small adjustments that should have a big impact. The notification that someone has deployed changes in the background is now much more discrete - and can be ignored entirely. Only when you want to deploy your changes will you need to merge in their changes first. This lets you stay in the zone whilst working, keeping the interruptions to a minimum.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr4-background-deploy-8bgFtZHjcw-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr4-background-deploy-8bgFtZHjcw-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the new background deploy notification" alt=""Screenshot of the new background deploy notification"" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr4-background-deploy-8bgFtZHjcw-650.jpeg" width="650" height="142" /></picture></p>
<h2 id="other-updates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/node-red-4-on-flowfuse-cloud/#other-updates"></a> Other Updates</h2>
<p>There are of course many other great reasons to start using Node-RED 4.0. The <a href="https://nodered.org/blog/2024/06/20/version-4-0-released">community blog post</a> describes all the other great features in this release.</p>
<h2 id="try-it-out-on-flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/node-red-4-on-flowfuse-cloud/#try-it-out-on-flowfuse-cloud"></a> Try it out on FlowFuse Cloud</h2>
<p>Node-RED 4.0 is available on FlowFuse Cloud right now. You can spin up a new instance, or upgrade any of your existing instances via the options under the Instance Settings page.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Node-RED%204%3A%20Bringing%20better%20collaboration%20to%20FlowFuse%20Cloud">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/FlowFuse 2.5: New features to visualize snapshots, LDAP integration, and moreEnhancing security, visualization, and deployment flexibility.2024-06-06T00:00:00Z<p>FlowFuse 2.5 introduces LDAP integration, snapshot comparison, extends the ability to preview flow to Blueprints, rounds out the management for snapshots, and allows you to point your own domain names at your FlowFuse instances.</p>
<!--more-->
<h2 id="support-for-ldap-integration-%232558" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#support-for-ldap-integration-%232558"></a> Support for LDAP integration <a href="https://github.com/FlowFuse/flowfuse/issues/2558">#2558</a></h2>
<p>In our commitment to making FlowFuse more versatile and secure, we've introduced support for LDAP alongside our existing SAML SSO options. This has been a much requested feature that we are excited to release. This allows organizations to manage user authentication seamlessly, leveraging their existing LDAP infrastructure. With this feature, you can ensure that access controls are robust and aligned with your company's security policies.</p>
<h2 id="compare-snapshots-visually-to-see-differences-%233624" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#compare-snapshots-visually-to-see-differences-%233624"></a> Compare Snapshots Visually to see differences <a href="https://github.com/FlowFuse/flowfuse/issues/3624">#3624</a></h2>
<p>Keeping track of changes and updates has never been easier. With our new visual comparison tool, you can now compare snapshots and see the differences at a glance. This feature provides a clear, graphical representation of changes, making it simple to identify modifications and understand their impact.</p>
<h2 id="preview-blueprints-before-deployment-%233838" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#preview-blueprints-before-deployment-%233838"></a> Preview Blueprints before Deployment <a href="https://github.com/FlowFuse/flowfuse/issues/3838">#3838</a></h2>
<p>Similarly to the feature to be able to compare snapshots we have enabled this same feature to allow users to preview team flows and blueprints prior to deploying. This expedites the process when you are attempting to find the correct flow for your application. We find that our users will be able to expedite the exploration of both their own team library and the blueprints.</p>
<h2 id="import-and-export-support-for-snapshots-%233628" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#import-and-export-support-for-snapshots-%233628"></a> Import and Export support for snapshots <a href="https://github.com/FlowFuse/flowfuse/issues/3628">#3628</a></h2>
<p>Managing your Node-RED artifacts just got simpler with our new import and export support for snapshots. With just a few clicks, you can easily transfer configurations between deployments or back up your current setup. This feature is perfect for those who need to replicate environments within their own enterprise with multiple deployments or ensure their configurations are safely stored.</p>
<h2 id="deploy-dashboard-and-apis-running-on-flowfuse-via-your-own-domain-names-%23324" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#deploy-dashboard-and-apis-running-on-flowfuse-via-your-own-domain-names-%23324"></a> Deploy Dashboard and APIs running on Flowfuse via your own Domain Names <a href="https://github.com/FlowFuse/flowfuse/issues/324">#324</a></h2>
<p>Branding and accessibility are crucial, and with FlowFuse 2.5, you can now deploy dashboards and APIs on your own domain names. This enhancement allows you to present a consistent brand experience and makes it easier for users to access your services. Whether you're deploying internally or externally, this feature provides the flexibility you need.</p>
<h2 id="full-list-of-release-features-and-bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#full-list-of-release-features-and-bug-fixes"></a> Full list of release features and bug fixes</h2>
<p>You can view everything included in 2.5 on the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.5.0">Github Release page</a>.</p>
<p>We also regularly release updates to <a href="https://app.flowfuse.com/">FlowFuse Cloud</a> in between our monthly releases. You can follow the updates as they are made via our <a href="https://flowfuse.com/changelog/">ChangeLog</a>.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 2.5.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/flowfuse-2-5-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/How to Use MQTT in Node-RED.Step-by-step guide on integrating MQTT with Node-RED.2024-06-05T00:00:00ZSumit Shinde<p>Every day, factories generate terabytes of data from sensors, PLCs, and production equipment. Yet most of this data remains locked in silos, making it difficult to spot trends, prevent failures, or optimize operations. Manufacturers need a practical way to connect their equipment and get data flowing where it is needed most.</p>
<!--more-->
<p>MQTT has become the industry standard for machine-to-machine communication because it is lightweight, reliable, and works even over limited bandwidth. When combined with Node-RED's drag-and-drop interface, engineers can create powerful data flows without writing complex code.</p>
<p>This guide walks through how to set up MQTT with Node-RED for industrial use cases—enabling real-time monitoring, alerting, and more.</p>
<h2 id="setting-up-your-mqtt-and-node-red-environment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#setting-up-your-mqtt-and-node-red-environment"></a> Setting up your MQTT and Node-RED Environment</h2>
<h3 id="setting-up-your-mqtt-environment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#setting-up-your-mqtt-environment"></a> Setting up your MQTT Environment</h3>
<p>In this guide, we will utilize the <a href="https://www.hivemq.com/mqtt-cloud-broker/">HiveMQ Cloud MQTT broker</a>. We will use their free trial as we are learning, but if you want to use it for your project, make sure to use their correct cluster according to your needs. If you prefer to use another cloud platform, such as Mosquitto or EMqX, feel free to do so.</p>
<p>FlowFuse has also launched an MQTT broker service that is integrated directly into the platform, making it easy to use MQTT with Node-RED. For more information, check out <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/10/announcement-mqtt-broker/">FlowFuse's MQTT Broker Announcement</a>.</p>
<ol>
<li>Create your account with HiveMQ and log in.</li>
<li>After logging in, you will be asked to select a cluster. Choose the <strong>Starter</strong> cluster, then select the <strong>AWS Cloud</strong> provider and set the tier to <strong>Production S</strong>. Make sure to choose the correct region and proceed to create the cluster.</li>
<li>Now, click on <strong>Manage Cluster</strong>, then click on the <strong>Access Management</strong> option located at the top. Create credentials in the <strong>Authentication</strong> tab with the correct permissions.</li>
</ol>
<h3 id="setting-up-your-node-red-environment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#setting-up-your-node-red-environment"></a> Setting up your Node-RED Environment</h3>
<p>For Node-RED, we will be using FlowFuse, which enhances Node-RED usage with managed instances, automated deployments, and real-time collaboration features, making it ideal for enterprise teams. It simplifies the configuration, monitoring, and operation of Node-RED applications.</p>
<ol>
<li>Head to the <a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20Use%20MQTT%20in%20Node-RED">FlowFuse sign-up</a> page to create your new account and the <a href="https://app.flowfuse.com/">FlowFuse login</a> page to log in.</li>
<li>After successful login, you will see the Application and instance, which is added by default.</li>
<li>Click on that instance and then click on the editor URL to open the Node-RED editor.</li>
</ol>
<h2 id="configuring-mqtt-node-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#configuring-mqtt-node-in-node-red"></a> Configuring MQTT Node in Node-RED</h2>
<p>When you open the Node-RED editor, you'll see the MQTT nodes already installed as they are part of Node-RED core nodes. When you drag an <strong>mqtt-in</strong> or <strong>mqtt-out</strong> node onto the workspace, you need to configure the MQTT broker node. You can do this by Double-clicking the mqtt node, clicking on the edit icon next to the Server field, and entering the following details of your MQTT broker into the MQTT broker config node; for more information about mqtt nodes, refer to the <a href="https://flowfuse.com/node-red/core-nodes/mqtt/">MQTT core node docs</a>.</p>
<h2 id="connection-tab" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#connection-tab"></a> Connection Tab</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-connection-tab-yAVEA2g8F9-729.avif 729w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-connection-tab-yAVEA2g8F9-729.webp 729w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of mqtt broker config node's connection tab" alt=""Screenshot of mqtt broker config node's connection tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-connection-tab-yAVEA2g8F9-729.jpeg" width="729" height="642" /></picture></p>
<ol>
<li>
<p>Enter the <strong>Server address</strong> from your HiveMQ cluster. For example: <code>yourclustername.a02.usw2.aws.hivemq.cloud</code>.</p>
</li>
<li>
<p>Set the <strong>Port</strong> to <code>8883</code> for secure connections in production. Use <code>1883</code> only for unencrypted testing.</p>
</li>
<li>
<p>Check the <strong>“Connect Automatically”</strong> option to allow Node-RED to reconnect if the connection drops.</p>
</li>
<li>
<p>Enable the <strong>“Use TLS”</strong> option to ensure the connection is encrypted. This is strongly recommended for production use.</p>
</li>
<li>
<p>Select <strong>MQTT Version 3.1.1</strong> from the protocol version dropdown.</p>
</li>
<li>
<p>Leave the Client ID field blank to auto-generate one, or enter a specific ID if your broker requires it.</p>
</li>
<li>
<p>Keep the <strong>Keep Alive</strong> interval at the default value of 60 seconds unless your Application requires a different interval.</p>
</li>
<li>
<p>Uncheck <strong>“Clean Session”</strong> if you want Node-RED to receive messages that were sent while it was offline.</p>
</li>
</ol>
<h3 id="security-tab" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#security-tab"></a> Security Tab</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-security-tab-gSb00UCsat-730.avif 730w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-security-tab-gSb00UCsat-730.webp 730w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of mqtt broker config node's security tab" alt=""Screenshot of mqtt broker config node's security tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-broker-security-tab-gSb00UCsat-730.jpeg" width="730" height="566" /></picture></p>
<ol>
<li>
<p>Enter the <strong>Username</strong> that you created in the HiveMQ Access Management tab. If you're using a different broker, use the appropriate username for that broker.</p>
</li>
<li>
<p>Enter the <strong>Password</strong> associated with that username.</p>
</li>
<li>
<p>Use <strong>environment variables</strong> for the username and password to avoid exposing sensitive information directly in the flow.</p>
</li>
</ol>
<blockquote>
<p>For more information, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment Variables in Node-RED</a>.</p>
</blockquote>
<h2 id="publishing-data-to-a-topic-on-mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#publishing-data-to-a-topic-on-mqtt-broker"></a> Publishing Data to a Topic on MQTT Broker</h2>
<ol>
<li>Drag an <strong>mqtt-out</strong> node onto the canvas.</li>
<li>Double-click on the <strong>mqtt-out</strong> node and select the added broker configuration to which you want to send data in the server field.</li>
<li>In the topic field, enter the desired topic name.</li>
<li>Set the <strong>QoS</strong> to <strong>2</strong> for accurate and guaranteed data delivery.</li>
<li>Set <strong>retain</strong> to true if you want to retain the data.</li>
<li>Connect the node's output, which is emitting the payload data you want to send to the MQTT broker, to the input of the <strong>mqtt-out</strong> node.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-mqtt-out-node--qK7g48UeC-582.avif 582w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-mqtt-out-node--qK7g48UeC-582.webp 582w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the mqtt-out node configuration" alt=""Screenshot of the mqtt-out node configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-mqtt-out-node--qK7g48UeC-582.jpeg" width="582" height="485" /></picture></p>
<h2 id="subscribing-to-a-topic-on-mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#subscribing-to-a-topic-on-mqtt-broker"></a> Subscribing to a Topic on MQTT Broker</h2>
<ol>
<li>Drag an <strong>MQTT-in</strong> node onto the canvas.</li>
<li>Double-click on the <strong>MQTT-in</strong> node and select the appropriate added broker configuration from which you want to receive data in the server field.</li>
<li>Set <strong>action</strong> to <strong>subscribe to a single topic</strong> and enter the topic name to which you want to subscribe for receiving data in the topic field.</li>
<li>Set the <strong>QoS</strong> to <strong>2</strong>.</li>
<li>Set the output to the desired format.</li>
<li>Connect the output of the <strong>mqtt-in</strong> node to the input of the node to whom you want to pass the data for further processing or analysis.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-mqtt-in-node-wO8shLZZdC-618.avif 618w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-mqtt-in-node-wO8shLZZdC-618.webp 618w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the mqtt-in node configuration" alt=""Screenshot of the mqtt-in node configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-mqtt-in-node-wO8shLZZdC-618.jpeg" width="618" height="569" /></picture></p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#deploying-the-flow"></a> Deploying the Flow</h2>
<ol>
<li>To deploy the flow, click on the <strong>deploy</strong> button located at the top-right corner.</li>
</ol>
<p><em>Tip: To ensure that your MQTT node is connected to the broker, check the node status. It will display 'connected' if the connection is successful.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-showing-mqtt-node-status-F-d0it804Z-729.avif 729w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-showing-mqtt-node-status-F-d0it804Z-729.webp 729w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the mqtt node status showing node is connected successfully to the broker" alt=""Screenshot of the mqtt node status showing node is connected successfully to the broker"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-showing-mqtt-node-status-F-d0it804Z-729.jpeg" width="729" height="278" /></picture></p>
<h2 id="creating-a-simple-project" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#creating-a-simple-project"></a> Creating a Simple Project</h2>
<p>Let’s build a practical temperature monitoring system to demonstrate how MQTT works in action. In this setup, one Node-RED instance will publish temperature data, and another will subscribe to and display it.</p>
<p>While MQTT is a reliable choice for communication across distributed systems, <strong>FlowFuse</strong> simplifies this even further with <a href="https://flowfuse.com/docs/user/projectnodes/">Project Link</a> nodes. These nodes enable direct data sharing between Node-RED instances—no need to configure external MQTT brokers or manage topics. Just link your instances and start exchanging messages. Behind the scenes, Project Link still uses MQTT but abstracts away the setup. This makes it especially useful when prototyping or when fast, instance-to-instance communication is needed.</p>
<p>For this example, we will use MQTT directly to illustrate how it fits into production environments where standard, interoperable protocols are essential. If you are running Node-RED on edge devices, refer to our <a href="https://flowfuse.com/node-red/hardware/raspberry-pi-4/">Raspberry Pi 4 guide</a> for setup instructions.</p>
<h3 id="publishing-temperature-data-to-a-topic-on-the-mqtt-broker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#publishing-temperature-data-to-a-topic-on-the-mqtt-broker"></a> Publishing Temperature Data to a Topic on the MQTT Broker</h3>
<ol>
<li>Drag an <strong>mqtt-out</strong> node onto the canvas and configure it with the MQTT broker to which you want to send data.</li>
<li>Enter "temp" in the topic field and set <strong>QoS</strong> to <strong>2</strong>.</li>
<li>Connect the output of the node that is reading your temperature data to the input of the <strong>mqtt-out</strong> node.</li>
<li>Deploy the flow by clicking on the <strong>deploy</strong> button located at the top-right corner.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-1-0XVaC0CZx--1852.avif 1852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-1-0XVaC0CZx--1852.webp 1852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the device editor of a Node-RED instance running on the device ( connected to a temperature sensor ) using the FlowFuse device agent, where we are reading temperature data from the sensor and sending it to an MQTT broker." alt=""Screenshot of the device editor of a Node-RED instance running on the device (connected to a temperature sensor) using the FlowFuse device agent, where we are reading temperature data from the sensor and sending it to an MQTT broker."" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-1-0XVaC0CZx--1852.jpeg" width="1852" height="842" /></picture></p>
<h3 id="subscribing-to-the-topic-on-the-mqtt-broker-to-receive-temperature-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#subscribing-to-the-topic-on-the-mqtt-broker-to-receive-temperature-data"></a> Subscribing to the Topic on the MQTT Broker to Receive Temperature Data</h3>
<p>Now, create a new instance in which we will receive the temperature data by subscribing to the Topic. Refer to this guide, which shows how you can <a href="https://flowfuse.com/docs/user/introduction/#creating-a-node-red-instance">create a new instance in FlowFuse</a>.</p>
<ol>
<li>Drag an <strong>mqtt-in</strong> node onto the canvas, and configure it with the broker to which you are sending temperature data.</li>
<li>Enter "temp" in the topic field and set <strong>QoS</strong> to <strong>2</strong>.</li>
<li>Drag a <strong>Debug</strong> node onto the canvas.</li>
<li>Connect the <strong>mqtt-in</strong> node's output to the <strong>Debug</strong> node's input.</li>
<li>Deploy the flow by clicking on the <strong>deploy</strong> button located at the top-right corner.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-2-HMRNV-pSQk-1846.avif 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-2-HMRNV-pSQk-1846.webp 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of sencod Node-RED instance editor in which temperature data is receving from mqtt broker" alt=""Screenshot of sencod Node-RED instance editor in which temperature data is receving from mqtt broker"" loading="lazy" decoding="async" src="https://flowfuse.com/img/how-to-use-mqtt-with-node-red-mqtt-node-red-instance-2-HMRNV-pSQk-1846.jpeg" width="1846" height="839" /></picture></p>
<p>Now, you will see the temperature data printed in the debug tab in the sidebar. Additionally, you can display this data on a chart using FlowFuse Dashboard or store it in a database. For more details, refer to the following guides:</p>
<ul>
<li><a href="https://flowfuse.com/node-red/database/influxdb/">Sending data to influxDB</a></li>
<li><a href="https://flowfuse.com/node-red/integration-technologies/rest/">Charting Data in on Dashboard 2.0</a></li>
</ul>
<h2 id="best-practices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#best-practices"></a> Best Practices</h2>
<p>Ensuring the security and efficiency of your MQTT and Node-RED deployments is crucial for successful IoT projects. Explore these best practices to enhance your system's performance and protect your data.</p>
<ul>
<li>
<p><strong>SSL/TLS Encryption:</strong> Secure your MQTT communication by enabling SSL/TLS encryption. This ensures that data transmitted between devices and the broker is encrypted.</p>
</li>
<li>
<p><strong>Authentication and Authorization:</strong> Implement strong authentication mechanisms to verify the identity of clients connecting to the broker. Additionally, enforce access control policies to restrict clients' actions based on their roles and permissions. For instance, you can allow specific clients to only publish or subscribe to data as needed.</p>
</li>
<li>
<p><strong>Environment Variables:</strong> Utilize environment variables to prevent exposing your sensitive configuration data within the flow.</p>
</li>
<li>
<p><strong>Quality of Service (QoS) Levels:</strong> Choose the appropriate QoS level for your MQTT messages based on the reliability requirements of your Application. Higher QoS levels ensure message delivery but may incur increased network overhead.</p>
</li>
<li>
<p><strong>Payload Size Optimization:</strong> Optimize the size of MQTT message payloads to minimize bandwidth usage and improve network efficiency. Transmit only essential data and consider compressing payloads when applicable to reduce transmission times.</p>
</li>
</ul>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/#conclusion"></a> Conclusion</h2>
<p>This guide showed how MQTT and Node-RED enable reliable industrial data communication. By following these patterns, manufacturers can connect diverse equipment and build real-time monitoring systems without complex custom code.</p>
<p>Start with one production line, prove the value, then scale across your facility using the same proven approach.</p>
<p>As you grow, you may face common challenges: managing multiple Node-RED instances, coordinating team changes, ensuring uptime, and handling backups. That’s where <strong>FlowFuse</strong> helps.</p>
<p>It adds:</p>
<ul>
<li>Centralized instance management</li>
<li>Easy scaling and deployment</li>
<li>Remote device management</li>
<li>Automatic backups and version control</li>
<li>Seamless team collaboration without conflicts</li>
<li>Enterprise-grade security (SSO, audit logs, RBAC)</li>
<li>High availability for 24/7 operations</li>
<li>Expert support when needed</li>
<li>And much more</li>
</ul>
<p>Your Node-RED flows work the same—FlowFuse just makes them easier to manage and production-ready.</p>
<p><a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20Use%20MQTT%20in%20Node-RED">Start your free trial</a> and see why manufacturers choose us for scaling with confidence.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/Exploring Node-RED Dashboard 2.0 WidgetsA guide to using Node-RED 2.0 Widgets for Dashboard Development.2024-05-27T00:00:00ZSumit Shinde<p>This guide delves into Node-RED Dashboard 2.0 widgets. It is a guide on how to build a Dashboard application, and will cover many of the widgets available today.</p>
<!--more-->
<p>If you're new to Dashboard 2.0, we recommend starting with the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started with Dashboard 2.0</a> guide and make sure to install it.</p>
<h2 id="what-are-widgets%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#what-are-widgets%3F"></a> What Are Widgets?</h2>
<p>Widgets in Node-RED Dashboard 2.0 are the building blocks for creating a user interface. In Dashboard 2.0, you get a variety of widgets like forms, templates, buttons, and others to make different parts of your interface.</p>
<h2 id="building-applications-with-dashboard-2.0-widgets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#building-applications-with-dashboard-2.0-widgets"></a> Building Applications with Dashboard 2.0 Widgets</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-incom-expense-tracker-system-_WyK50jUNb-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Income-expense tracker build with dashboard 2.0" alt=""Income-expense tracker build with dashboard 2.0"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-incom-expense-tracker-system-_WyK50jUNb-800.webp" width="800" height="450" /></picture>
<em>Income-expense tracker build with dashboard 2.0</em></p>
<p>In this guide, we'll create a basic application to input expenses and income. This will then be displayed in a chart and table for analysis. The application will utilize a wide range of widgets available in Dashboard 2.0, helping you understand and use them confidently.</p>
<h3 id="adding-forms" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#adding-forms"></a> Adding Forms</h3>
<p>For the income and expense submission, we'll incorporate a form using the <strong>ui-form</strong> widget.</p>
<ol>
<li>Drag the <strong>ui-form</strong> widget onto the canvas.</li>
<li>Double-click on it to access various widget properties and select the <strong>ui-group</strong> where it should render.</li>
<li>Add "date", "description", "amount", and "note" form elements by clicking the <strong>+element</strong> button at the bottom left.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-income-submission-form-upi-UZLBaV-935.avif 935w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-income-submission-form-upi-UZLBaV-935.webp 935w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying Income submission ui-form's configuration" alt=""Screenshot displaying Income submission ui-form's configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-income-submission-form-upi-UZLBaV-935.jpeg" width="935" height="838" /></picture>
<em>Screenshot displaying Income submission ui-form's configuration</em></p>
<p>Once you've added the income submission form, repeat the process to add an expense submission form on another <strong>ui-page</strong> and <strong>ui-group</strong>. For more information on <strong>ui-form</strong>, refer to the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-form.html">ui-form docs</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-expense-submission-form-cH-FRaRmJF-933.avif 933w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-expense-submission-form-cH-FRaRmJF-933.webp 933w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying Expense submission ui-form's configuration" alt=""Screenshot displaying Expense submission ui-form's configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-expense-submission-form-cH-FRaRmJF-933.jpeg" width="933" height="834" /></picture>
<em>Screenshot displaying Expense submission ui-form's configuration</em></p>
<h3 id="storing-form-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#storing-form-data"></a> Storing Form Data</h3>
<p>The <strong>ui-form</strong> widget emits a payload object with key-value pairs of form elements upon submission. We'll store this data in a global context, If you are not familiar with Node-RED context, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/">Understanding Node-RED varriables</a>.</p>
<ol>
<li>Drag a <strong>function</strong> node onto the canvas and add the following code. This will store the submission in the <code>income</code> global context variable, and then modify <code>msg.payload</code> to pass on a notification to any further connected nodes.</li>
</ol>
<div style="position: relative" id="code-container-67">
<pre class="language-javascript"><code id="code-67" class="language-javascript"><span class="token comment">// Retrieve the existing 'income' array from the global context, or initialize it as an empty array if it doesn't exist</span><br /><span class="token keyword">let</span> income <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'income'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Push the incoming payload along with a 'type' property set to "income" into the 'income' array</span><br />income<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token operator">...</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">,</span><br /> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">"income"</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Store the updated 'income' array back into the global context</span><br />global<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'income'</span><span class="token punctuation">,</span> income<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Set the message payload to a confirmation message for notification</span><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> <span class="token string">"Thank you for submitting income!"</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Return the modified message</span><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-67" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Similarly, you can do this for storing expense data submitted using the expense submission form.</p>
<h3 id="displaying-notifications" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#displaying-notifications"></a> Displaying Notifications</h3>
<p>For displaying notifications on the dashboard, we'll utilize the <strong>ui-notification</strong> widget, which emits notifications to the user's dashboard. It accepts <code>msg.payload</code> which should be a string format or raw HTML/JavaScript for custom formatting.</p>
<ol>
<li>Drag the <strong>ui-notification</strong> widget onto the canvas.</li>
<li>Set the position property to <strong>center</strong>. You can also adjust colors or notification timeout by modifying the color and timeout properties. Please take a look at the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-notification.html#properties">ui-notification docs</a> for more information on <strong>ui-notification</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-notification-widget-yy3cUfn9-8-513.avif 513w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-notification-widget-yy3cUfn9-8-513.webp 513w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying ui-notification widgets configuration" alt=""Screenshot displaying ui-notification widgets configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-notification-widget-yy3cUfn9-8-513.jpeg" width="513" height="695" /></picture>
<em>Screenshot displaying ui-notification widgets configuration</em></p>
<h3 id="listening-for-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#listening-for-events"></a> Listening for events</h3>
<p>In Dashboard 2.0, the <strong>ui-event</strong> widget allows you to listen to user behavior or events. It does not render any content or components on the dashboard. Currently, this widget only listens for page views (<code>$pageview</code>) and leave (<code>$pageleave</code>) events.</p>
<p>With this, we can listen for page view and page leave events and trigger tasks based on those events. For instance, in our application, we will be displaying a table containing income and expense data, along with a chart. We'll update them when navigating to a new page or leaving a page.</p>
<ol>
<li>Drag an <strong>ui-event</strong> widget onto the canvas.</li>
<li>Double-click on it and select the correct <strong>ui-base</strong> of your application.</li>
</ol>
<p>For more information on ui-event refer to <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-event.html">ui-event docs</a>.</p>
<h3 id="retrieving-income-expense-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#retrieving-income-expense-data"></a> Retrieving Income-Expense Data</h3>
<p>In our income-expense application, we will display the income and expenses in a single table.</p>
<ol>
<li>Drag a <strong>change</strong> node onto the canvas.</li>
<li>Set <code>msg.payload</code> to the JSONata expression below, which merges the income and expense arrays.</li>
</ol>
<div style="position: relative" id="code-container-134">
<pre class="language-javascript"><code id="code-134" class="language-javascript"><span class="token punctuation">[</span><span class="token function">$globalContext</span><span class="token punctuation">(</span><span class="token string">'income'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">$globalContext</span><span class="token punctuation">(</span><span class="token string">'expense'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-134" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the output of the <strong>ui-event</strong> widget to the input of the <strong>change</strong> node.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-change-node-bTjBsSKM3o-580.avif 580w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-change-node-bTjBsSKM3o-580.webp 580w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node setting JSON expression to payload for retrieving and sorting data." alt=""Screenshot displaying the change node setting JSON expression to payload for retrieving and sorting data."" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-change-node-bTjBsSKM3o-580.jpeg" width="580" height="534" /></picture>
<em>Screenshot displaying the change node setting JSON expression to payload for retrieving and sorting data.</em></p>
<h3 id="displaying-data-on-the-table" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#displaying-data-on-the-table"></a> Displaying Data on the Table</h3>
<p>To display data on the table, we use the <strong>ui-table</strong> widget in Dashboard 2.0. This widget accepts an array of objects as input. The columns in the table correspond to the properties of the objects within the array, and each row represents a different object with values corresponding to those properties.</p>
<ol>
<li>Drag a <strong>ui-table</strong> widget onto the canvas.</li>
<li>Create a new <strong>ui-page</strong> and <strong>ui-group</strong> for it.</li>
<li>Connect the output of the <strong>change</strong> node to the input of the <strong>ui-table</strong> widget.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-table-widget-kizhg9FNuC-513.avif 513w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-table-widget-kizhg9FNuC-513.webp 513w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the ui-table widget configuration" alt=""Screenshot displaying the ui-table widget configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-table-widget-kizhg9FNuC-513.jpeg" width="513" height="656" /></picture>
<em>Screenshot displaying the ui-table widget configuration</em></p>
<p>For more information on ui-table refer to <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-table.html">ui-table docs</a></p>
<h3 id="calculating-total-category-wise" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#calculating-total-category-wise"></a> Calculating total category-wise</h3>
<p>In our application, we will display data on the chart, showing the total income and total expenses for analysis. In this section, we will calculate the total expenses and income using the function node.</p>
<ol>
<li>Drag the two <strong>change</strong> node onto the canvas.</li>
<li>For the first <strong>Change</strong> node Set <code>msg.payload</code> to <code>global.income</code> and <code>msg.topic</code> to "income" and give it name "retrive income". For the second <strong>Change</strong> node, set <code>msg.payload</code> to <code>global.expense</code> and <code>msg.topic</code> to "expense" and give that second change node name "retrive expense".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-income-change-node-gri3ddBzSs-417.avif 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-income-change-node-gri3ddBzSs-417.webp 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node retrieving income data from global context" alt=""Screenshot displaying the change node retrieving income data from global context"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-income-change-node-gri3ddBzSs-417.jpeg" width="417" height="388" /></picture>
<em>Screenshot displaying the change node retrieving income data from global context</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-expense-change-node-0swN79D6D4-417.avif 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-expense-change-node-0swN79D6D4-417.webp 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node retrieving expense data from global context" alt=""Screenshot displaying the change node retrieving expense data from global context"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-expense-change-node-0swN79D6D4-417.jpeg" width="417" height="418" /></picture>
<em>Screenshot displaying the change node retrieving expense data from global context</em></p>
<ol start="3">
<li>Drag a <strong>Split</strong> node onto the canvas.</li>
<li>Drag the <strong>Change</strong> node onto the canvas and set <code>msg.payload.amount</code> to the JSONata expression <code>$number(payload.amount)</code> and give it name "Convert amount to number".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-convert-amount-to-number-change-node-o23pvVbUWT-417.avif 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-convert-amount-to-number-change-node-o23pvVbUWT-417.webp 417w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node converting amount to number" alt=""Screenshot displaying the change node converting amount to number"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-convert-amount-to-number-change-node-o23pvVbUWT-417.jpeg" width="417" height="348" /></picture>
<em>Screenshot displaying the change node converting amount to number</em></p>
<ol start="5">
<li>Drag a <strong>Join</strong> node onto the canvas, select mode as <strong>reduced expression</strong>, and set the <strong>Reduce exp</strong> to <code>$A + payload.amount</code>. Set Initial value to <code>0</code>, and <strong>Fix-up exp</strong> to <code>$A</code>. Give this <strong>join</strong> node the name "Calculate total". This function operates similarly to using the javascript reduce method on an array to calculate the sum of its values. <code>$A</code> stores the accumulated value, and with every incoming message payload, it adds the <code>payload.amount</code> value to it, for more details on this refer to the <a href="https://flowfuse.com/node-red/core-nodes/join/">core node docs on join node</a>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-calculate-total-join-node-phSGXjVPV0-398.avif 398w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-calculate-total-join-node-phSGXjVPV0-398.webp 398w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the join node calculating the total income and expense data" alt=""Screenshot displaying the join node calculating the total income and expense data"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-calculate-total-join-node-phSGXjVPV0-398.jpeg" width="398" height="441" /></picture>
<em>Screenshot displaying the join node calculating the total income and expense data</em></p>
<ol start="7">
<li>Drag an another join node onto the canvas set mode to manual, combine each to complete message, to create to array and After a number of message parts to 2 and give it name "combine two objects into array".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-combine-object-join-node-Q9-6CnJxIV-392.avif 392w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-combine-object-join-node-Q9-6CnJxIV-392.webp 392w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the join node combining the income and expense object into the" alt=""Screenshot displaying the join node combining the income and expense object into the array"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-retrieve-combine-object-join-node-Q9-6CnJxIV-392.jpeg" width="392" height="453" /></picture>
<em>Screenshot displaying the join node combining the income and expense object into the array</em></p>
<ol start="7">
<li>
<p>Connect the output of the <strong>ui-event</strong> widget to the input of the <strong>Change</strong> node named "Retrieve Income" and "Retrieve Expense". Then, connect the outputs of the "Retrieve Income" and "Retrieve Expense" <strong>Change</strong> nodes to the input of the <strong>Split</strong> node.</p>
</li>
<li>
<p>Next, connect the output of the <strong>Split</strong> node to the <strong>Change</strong> node named "Convert Amount to Number". Afterward, connect the output of that <strong>Change</strong> node to the input of the <strong>Join</strong> node named "Calculate Total". Finally, connect the output of the "Calculate Total" <strong>Join</strong> node to the input of the <strong>Join</strong> node named "Combine Objects into Array".</p>
</li>
</ol>
<h3 id="displaying-data-on-the-chart" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#displaying-data-on-the-chart"></a> Displaying data on the chart</h3>
<p>To display charts on the dashboard, we have to use the ui-chart widget which allows us to display different types of charts on a dashboard including linear, bar, scatter, etc. This accepts an array and object as input.</p>
<ol>
<li>Drag a <strong>ui-chart</strong> widget onto the canvas.</li>
<li>Double-click on the widget and select Type as <strong>bar</strong>.</li>
<li>Configure the series to <strong>category</strong> and the y-axis to <strong>amount</strong>. This configuration informs the chart that the <strong>amount</strong> property of the input objects will be plotted on the y-axis and category to the x-axis of the chart.</li>
<li>Connect the output of the <strong>join</strong> node named "Combine Objects into Array" to the <strong>ui-chart</strong> widget's input.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-pNqeFPKwKa-758.avif 758w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-pNqeFPKwKa-758.webp 758w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the ui-chart widget's configuration" alt=""Screenshot displaying the ui-chart widget's configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-chart-widget-pNqeFPKwKa-758.jpeg" width="758" height="836" /></picture>
<em>Screenshot displaying the ui-chart widget's configuration</em></p>
<h3 id="adding-custom-footer-with-ui-template" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#adding-custom-footer-with-ui-template"></a> Adding custom footer with ui-template</h3>
<p>With the <strong>ui-template</strong> widget, we can add a custom component to our app using Vue.js. It also allows adding custom CSS for the dashboard and lot of other things. For more information refer to <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html">ui-template docs</a>.</p>
<p>Using this widget, we will add a footer to our application.</p>
<ol>
<li>Drag an <strong>ui-template</strong> widget onto the canvas.</li>
<li>Set the widget type (scoped UI) that will render this widget on the entire dashboard, eliminating the need to add separate footers for each page of the dashboard.</li>
<li>Insert the following vue.js code in the <strong>ui-template</strong> widget.</li>
</ol>
<div style="position: relative" id="code-container-302">
<pre class="language-javascript"><code id="code-302" class="language-javascript"><span class="token operator"><</span>template<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> Footer Component <span class="token operator">--</span><span class="token operator">></span><br /> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"footer"</span><span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> Description <span class="token keyword">of</span> the Income<span class="token operator">-</span>Expense Tracker <span class="token operator">--</span><span class="token operator">></span><br /> <span class="token operator"><</span>div<span class="token operator">></span><br /> Welcome to our comprehensive income expense tracker<span class="token operator">!</span> Take control <span class="token keyword">of</span> your finances by monitoring your income and<br /> expenses effortlessly<span class="token punctuation">.</span> Our user<span class="token operator">-</span>friendly <span class="token keyword">interface</span> <span class="token class-name">makes</span> it simple to record transactions<span class="token punctuation">,</span> categorize expenses<span class="token punctuation">,</span> and<br /> analyze your financial trends<span class="token punctuation">.</span> With real<span class="token operator">-</span>time insights into your spending habits<span class="token punctuation">,</span> you can make smarter financial<br /> decisions and achieve your money goals faster<span class="token punctuation">.</span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> Copyright Information <span class="token operator">--</span><span class="token operator">></span><br /> <span class="token operator"><</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"copyright"</span><span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span> Display Current Year and Copyright Information <span class="token operator">--</span><span class="token operator">></span><br /> <span class="token number">2024</span> — <span class="token operator"><</span>strong<span class="token operator">></span>Vuetify<span class="token operator"><</span><span class="token operator">/</span>strong<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span><br /><span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span><br /><span class="token operator"><</span>style scoped<span class="token operator">></span><br /> <span class="token comment">/* Make the footer occupy all available space */</span><br /> <span class="token punctuation">.</span>footer <span class="token punctuation">{</span><br /> <span class="token literal-property property">position</span><span class="token operator">:</span>absolute<span class="token punctuation">;</span><br /> <span class="token literal-property property">bottom</span><span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">;</span><br /> background<span class="token operator">-</span>color<span class="token operator">:</span><span class="token function">rgb</span><span class="token punctuation">(</span><span class="token number">26</span><span class="token punctuation">,</span><span class="token number">26</span><span class="token punctuation">,</span><span class="token number">26</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token literal-property property">color</span><span class="token operator">:</span><span class="token function">rgb</span><span class="token punctuation">(</span><span class="token number">238</span><span class="token punctuation">,</span><span class="token number">238</span><span class="token punctuation">,</span><span class="token number">238</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token literal-property property">height</span><span class="token operator">:</span>130px<span class="token punctuation">;</span><br /> text<span class="token operator">-</span>align<span class="token operator">:</span>center<span class="token punctuation">;</span><br /> <span class="token literal-property property">padding</span><span class="token operator">:</span>14px<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token punctuation">.</span>copyright<span class="token punctuation">{</span><br /> margin<span class="token operator">-</span>top<span class="token operator">:</span>10px<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /><span class="token operator"><</span><span class="token operator">/</span>style<span class="token operator">></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-302" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-template-widget-F6mr4PahXa-511.avif 511w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/exploring-dashboard-2-widgets-template-widget-F6mr4PahXa-511.webp 511w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the ui-template widget's configuration" alt=""Screenshot displaying the ui-template widget's configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exploring-dashboard-2-widgets-template-widget-F6mr4PahXa-511.jpeg" width="511" height="705" /></picture></p>
<h3 id="deploying-your-application-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#deploying-your-application-flow"></a> Deploying your application flow</h3>
<div id="nr-flow-135" style="height: 450px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow135 = "\n[{\"id\":\"301c1fd8e29c3aae\",\"type\":\"ui-template\",\"z\":\"7ac3890dfa74703b\",\"group\":\"\",\"page\":\"\",\"ui\":\"a0a85a5f4c29af50\",\"name\":\"Footer\",\"order\":0,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"<template>\\n <!-- Footer Component -->\\n <div class=\\\"footer\\\">\\n <!-- Description of the Income-Expense Tracker -->\\n <div>\\n Welcome to our comprehensive income expense tracker! Take control of your finances by monitoring your income and\\n expenses effortlessly. Our user-friendly interface makes it simple to record transactions, categorize expenses,\\n and\\n analyze your financial trends. With real-time insights into your spending habits, you can make smarter financial\\n decisions and achieve your money goals faster.\\n </div>\\n <!-- Copyright Information -->\\n <div class=\\\"copyright\\\">\\n <!-- Display Current Year and Copyright Information -->\\n 2024 — <strong>Vuetify</strong>\\n </div>\\n </div>\\n</template>\\n\\n<style scoped>\\n /* Make the footer occupy all available space */\\n .footer {\\n position: absolute;\\n bottom: 0;\\n background-color: rgb(26, 26, 26);\\n color: rgb(238, 238, 238);\\n height: 130px;\\n text-align: center;\\n padding: 14px;\\n }\\n\\n .copyright {\\n margin-top: 10px;\\n }\\n</style>\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"widget:ui\",\"className\":\"\",\"x\":910,\"y\":700,\"wires\":[[]]},{\"id\":\"342f3ee215d32fdc\",\"type\":\"group\",\"z\":\"7ac3890dfa74703b\",\"name\":\"New Icome page\",\"style\":{\"label\":true},\"nodes\":[\"d028c878350d19e1\",\"43e79bb77d718c95\",\"5ba5d8bff1a77bea\"],\"x\":274,\"y\":279,\"w\":752,\"h\":82},{\"id\":\"d028c878350d19e1\",\"type\":\"ui-form\",\"z\":\"7ac3890dfa74703b\",\"g\":\"342f3ee215d32fdc\",\"name\":\"Income Submission Form\",\"group\":\"961528943e1bb698\",\"label\":\"\",\"order\":1,\"width\":\"0\",\"height\":\"0\",\"options\":[{\"label\":\"Date\",\"key\":\"date\",\"type\":\"date\",\"required\":true,\"rows\":null},{\"label\":\"Description\",\"key\":\"description\",\"type\":\"text\",\"required\":true,\"rows\":null},{\"label\":\"Amount\",\"key\":\"amount\",\"type\":\"number\",\"required\":true,\"rows\":null},{\"label\":\"Note\",\"key\":\"note\",\"type\":\"text\",\"required\":false,\"rows\":null}],\"formValue\":{\"date\":\"\",\"description\":\"\",\"amount\":\"\",\"note\":\"\"},\"payload\":\"\",\"submit\":\"submit\",\"cancel\":\"clear\",\"resetOnSubmit\":true,\"topic\":\"topic\",\"topicType\":\"msg\",\"splitLayout\":\"\",\"className\":\"\",\"x\":410,\"y\":320,\"wires\":[[\"5ba5d8bff1a77bea\"]]},{\"id\":\"43e79bb77d718c95\",\"type\":\"ui-notification\",\"z\":\"7ac3890dfa74703b\",\"g\":\"342f3ee215d32fdc\",\"ui\":\"a0a85a5f4c29af50\",\"position\":\"center center\",\"colorDefault\":true,\"color\":\"#000000\",\"displayTime\":\"3\",\"showCountdown\":true,\"outputs\":0,\"allowDismiss\":true,\"dismissText\":\"Close\",\"raw\":false,\"className\":\"\",\"name\":\"\",\"x\":910,\"y\":320,\"wires\":[]},{\"id\":\"5ba5d8bff1a77bea\",\"type\":\"function\",\"z\":\"7ac3890dfa74703b\",\"g\":\"342f3ee215d32fdc\",\"name\":\"Store income\",\"func\":\"let income = global.get('income') || [];\\n\\nincome.push({\\n ...msg.payload,\\n type:\\\"income\\\",\\n});\\n\\nglobal.set('income', income);\\n\\nmsg.payload = \\\"Thank you for submitting income!\\\"\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":650,\"y\":320,\"wires\":[[\"43e79bb77d718c95\"]]},{\"id\":\"961528943e1bb698\",\"type\":\"ui-group\",\"name\":\"Income Submission Form\",\"page\":\"d954d73f9dcd1472\",\"width\":\"12\",\"height\":\"1\",\"order\":1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"d954d73f9dcd1472\",\"type\":\"ui-page\",\"name\":\"New Income\",\"ui\":\"a0a85a5f4c29af50\",\"path\":\"/new-Icome\",\"icon\":\"bank-transfer-in\",\"layout\":\"notebook\",\"theme\":\"aeeec3fc1077eb1c\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"aeeec3fc1077eb1c\",\"type\":\"ui-theme\",\"name\":\"dashboard\",\"colors\":{\"surface\":\"#1a1a1a\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}},{\"id\":\"4afb4814be56a9a8\",\"type\":\"group\",\"z\":\"7ac3890dfa74703b\",\"name\":\"New Expense page\",\"style\":{\"label\":true},\"nodes\":[\"e82b719d899cbf73\",\"3457848652ad75e1\",\"bd90a9ad612408d3\"],\"x\":1054,\"y\":279,\"w\":752,\"h\":82},{\"id\":\"e82b719d899cbf73\",\"type\":\"ui-form\",\"z\":\"7ac3890dfa74703b\",\"g\":\"4afb4814be56a9a8\",\"name\":\"Expense Submission Form\",\"group\":\"854706651cd8a8f2\",\"label\":\"\",\"order\":1,\"width\":\"12\",\"height\":\"1\",\"options\":[{\"label\":\"Date\",\"key\":\"date\",\"type\":\"date\",\"required\":true,\"rows\":null},{\"label\":\"Description\",\"key\":\"description\",\"type\":\"text\",\"required\":true,\"rows\":null},{\"label\":\"Category\",\"key\":\"category\",\"type\":\"text\",\"required\":true,\"rows\":null},{\"label\":\"Amount\",\"key\":\"amount\",\"type\":\"number\",\"required\":true,\"rows\":null},{\"label\":\"Note\",\"key\":\"note\",\"type\":\"text\",\"required\":false,\"rows\":null}],\"formValue\":{\"date\":\"\",\"description\":\"\",\"category\":\"\",\"amount\":\"\",\"note\":\"\"},\"payload\":\"\",\"submit\":\"submit\",\"cancel\":\"clear\",\"resetOnSubmit\":true,\"topic\":\"topic\",\"topicType\":\"msg\",\"splitLayout\":\"\",\"className\":\"\",\"x\":1190,\"y\":320,\"wires\":[[\"bd90a9ad612408d3\"]]},{\"id\":\"3457848652ad75e1\",\"type\":\"ui-notification\",\"z\":\"7ac3890dfa74703b\",\"g\":\"4afb4814be56a9a8\",\"ui\":\"a0a85a5f4c29af50\",\"position\":\"center center\",\"colorDefault\":true,\"color\":\"#000000\",\"displayTime\":\"3\",\"showCountdown\":true,\"outputs\":0,\"allowDismiss\":true,\"dismissText\":\"Close\",\"raw\":false,\"className\":\"\",\"name\":\"\",\"x\":1690,\"y\":320,\"wires\":[]},{\"id\":\"bd90a9ad612408d3\",\"type\":\"function\",\"z\":\"7ac3890dfa74703b\",\"g\":\"4afb4814be56a9a8\",\"name\":\"Store expense\",\"func\":\"let expense = global.get('expense') || [];\\n\\nexpense.push({\\n ...msg.payload,\\n type: \\\"expense\\\",\\n});\\n\\nglobal.set('expense', expense);\\n\\nmsg.payload = \\\"Thank you for submitting expense!\\\"\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1420,\"y\":320,\"wires\":[[\"3457848652ad75e1\"]]},{\"id\":\"854706651cd8a8f2\",\"type\":\"ui-group\",\"name\":\"Expense Submission Form\",\"page\":\"97bf3e87f4bdddc1\",\"width\":\"12\",\"height\":\"1\",\"order\":2,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"97bf3e87f4bdddc1\",\"type\":\"ui-page\",\"name\":\"New Expense\",\"ui\":\"a0a85a5f4c29af50\",\"path\":\"/new-expense\",\"icon\":\"bank-transfer-out\",\"layout\":\"notebook\",\"theme\":\"aeeec3fc1077eb1c\",\"order\":2,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"77217e256ef75328\",\"type\":\"group\",\"z\":\"7ac3890dfa74703b\",\"name\":\"Your icome and expense table\",\"style\":{\"label\":true},\"nodes\":[\"452d561bf79727cb\",\"0c64567c81dd6a8d\"],\"x\":274,\"y\":399,\"w\":572,\"h\":82},{\"id\":\"452d561bf79727cb\",\"type\":\"ui-table\",\"z\":\"7ac3890dfa74703b\",\"g\":\"77217e256ef75328\",\"group\":\"e848a5e48a6549c9\",\"name\":\"\",\"label\":\"text\",\"order\":2,\"width\":0,\"height\":0,\"maxrows\":0,\"passthru\":false,\"autocols\":true,\"selectionType\":\"click\",\"columns\":[],\"x\":770,\"y\":440,\"wires\":[[]]},{\"id\":\"0c64567c81dd6a8d\",\"type\":\"change\",\"z\":\"7ac3890dfa74703b\",\"g\":\"77217e256ef75328\",\"name\":\"Merge income and expense data\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[ $globalContext(\\\"income\\\"),\\t $globalContext(\\\"expense\\\")\\t]\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":440,\"y\":440,\"wires\":[[\"452d561bf79727cb\"]]},{\"id\":\"e848a5e48a6549c9\",\"type\":\"ui-group\",\"name\":\"Your Income and Expense\",\"page\":\"7abf0b3cb6f38ca3\",\"width\":\"12\",\"height\":\"5\",\"order\":2,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"7abf0b3cb6f38ca3\",\"type\":\"ui-page\",\"name\":\"Your Income and expense\",\"ui\":\"a0a85a5f4c29af50\",\"path\":\"/your-icome-expense\",\"icon\":\"calendar-multiple-check\",\"layout\":\"grid\",\"theme\":\"aeeec3fc1077eb1c\",\"order\":3,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"1b78769d6d27d102\",\"type\":\"group\",\"z\":\"7ac3890dfa74703b\",\"name\":\"Event lister\",\"style\":{\"label\":true},\"nodes\":[\"9ce6db04b2c9d7b2\"],\"x\":74,\"y\":479,\"w\":152,\"h\":82},{\"id\":\"9ce6db04b2c9d7b2\",\"type\":\"ui-event\",\"z\":\"7ac3890dfa74703b\",\"g\":\"1b78769d6d27d102\",\"ui\":\"a0a85a5f4c29af50\",\"name\":\"\",\"x\":150,\"y\":520,\"wires\":[[\"0c64567c81dd6a8d\",\"2ca552c7e56b3619\",\"a7360e4a62bfc518\"]]},{\"id\":\"32502807f24f79b8\",\"type\":\"group\",\"z\":\"7ac3890dfa74703b\",\"name\":\"Overview chart\",\"style\":{\"label\":true},\"nodes\":[\"424b35900722e740\",\"058a77a65653d3f8\",\"7246228e57ac1fc5\",\"2ca552c7e56b3619\",\"23a4a7d15b44876b\",\"a7360e4a62bfc518\",\"fe308581b8a5b9f1\"],\"x\":274,\"y\":519,\"w\":1392,\"h\":142},{\"id\":\"424b35900722e740\",\"type\":\"ui-chart\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"group\":\"54eca83feb7c1479\",\"name\":\"Overview Chart\",\"label\":\"chart\",\"order\":2,\"chartType\":\"bar\",\"category\":\"topic\",\"categoryType\":\"property\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"category\",\"yAxisProperty\":\"payload\",\"ymin\":\"\",\"ymax\":\"\",\"action\":\"replace\",\"pointShape\":\"circle\",\"pointRadius\":4,\"showLegend\":false,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":[\"#1eb33c\",\"#aec7e8\",\"#ff7f0e\",\"#5f2ed1\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"width\":\"12\",\"height\":\"6\",\"className\":\"\",\"x\":1560,\"y\":600,\"wires\":[[]]},{\"id\":\"058a77a65653d3f8\",\"type\":\"join\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"Calculate total\",\"mode\":\"reduce\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\\\n\",\"joinerType\":\"str\",\"accumulate\":true,\"timeout\":\"\",\"count\":\"\",\"reduceRight\":false,\"reduceExp\":\"$A+payload.amount\",\"reduceInit\":\"0\",\"reduceInitType\":\"num\",\"reduceFixup\":\"$A\",\"x\":1000,\"y\":600,\"wires\":[[\"fe308581b8a5b9f1\"]]},{\"id\":\"7246228e57ac1fc5\",\"type\":\"split\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"\",\"splt\":\"\\\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":550,\"y\":600,\"wires\":[[\"23a4a7d15b44876b\"]]},{\"id\":\"2ca552c7e56b3619\",\"type\":\"change\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"Retrieve income\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"income\",\"tot\":\"global\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"income\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":380,\"y\":560,\"wires\":[[\"7246228e57ac1fc5\"]]},{\"id\":\"23a4a7d15b44876b\",\"type\":\"change\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"Convert amount to number\",\"rules\":[{\"t\":\"set\",\"p\":\"payload.amount\",\"pt\":\"msg\",\"to\":\"$number(payload.amount)\\t\",\"tot\":\"jsonata\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":760,\"y\":600,\"wires\":[[\"058a77a65653d3f8\"]]},{\"id\":\"a7360e4a62bfc518\",\"type\":\"change\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"Retrieve income\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"expense\",\"tot\":\"global\"},{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"expense\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":380,\"y\":620,\"wires\":[[\"7246228e57ac1fc5\"]]},{\"id\":\"fe308581b8a5b9f1\",\"type\":\"join\",\"z\":\"7ac3890dfa74703b\",\"g\":\"32502807f24f79b8\",\"name\":\"Combine two object into one array\",\"mode\":\"custom\",\"build\":\"array\",\"property\":\"\",\"propertyType\":\"full\",\"key\":\"topic\",\"joiner\":\"\\\\n\",\"joinerType\":\"str\",\"accumulate\":false,\"timeout\":\"\",\"count\":\"2\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":1270,\"y\":600,\"wires\":[[\"424b35900722e740\"]]},{\"id\":\"54eca83feb7c1479\",\"type\":\"ui-group\",\"name\":\"Overview\",\"page\":\"47bde79e946933d2\",\"width\":\"12\",\"height\":\"4\",\"order\":-1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"47bde79e946933d2\",\"type\":\"ui-page\",\"name\":\"Overview\",\"ui\":\"a0a85a5f4c29af50\",\"path\":\"/overview\",\"icon\":\"google-analytics\",\"layout\":\"notebook\",\"theme\":\"aeeec3fc1077eb1c\",\"order\":-1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"a0a85a5f4c29af50\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\",\"ui-chart\",\"ui-table\"],\"showPathInSidebar\":false,\"navigationStyle\":\"icon\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow135.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-135') })</script>
<ol>
<li>Deploy the flow by clicking the top right <strong>Deploy</strong> button.</li>
<li>Locate the *<strong>Open Dashboard</strong> button at the top-right corner of the Dashboard 2.0 sidebar and click on it to navigate to the dashboard.</li>
</ol>
<p>Now that we've built an income-expense tracker application and gained a basic understanding of Dashboard 2.0 widgets for building dashboards.</p>
<h3 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/exploring-node-red-dashboard-2-widgets/#up-next"></a> Up next</h3>
<p>If you want to enhance this simple application by adding more features or want to make the application personalize for users, consider the following resources:</p>
<ul>
<li>
<p><a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">Webinar</a> - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/">Comprehensive guide: Node-RED Dashboard 2.0 layout, sidebar, and styling</a> - This comprehensive guide will cover everything about Node-RED Dashboard 2.0 styling, layouts and sidebars.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/">Displaying logged-in users on Dashboard 2.0</a> - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/">How to Build an Admin Dashboard with Node-RED Dashboard 2.0</a> - This detailed guide demonstrates how to build a secure admin page in Node-RED Dashboard 2.0.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/">How to Build An Application With Node-RED Dashboard 2.0</a> - This guide, covers how you can build personalize multiuser dashboard using flow fuse multi-user addon.</p>
</li>
<li>
<p><a href="https://flowfuse.com/blueprints/flowfuse-dashboard/multi-user-dashboard/">Multi-User Dashboard for Ticket/Task Management blueprint</a> - this blueprint 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.</p>
</li>
</ul>
<p>If you are looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team, <a href="https://flowfuse.com/">sign up</a> for FlowFuse and get started today!</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Exploring%20Node-RED%20Dashboard%202.0%20Widgets">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/Why you need a low-code platformEmpowering Domain Experts with Low-Code Platforms: A Path to Seamless Digital Transformation2024-05-22T00:00:00Z<p>Digital transformation is a series of technological advancements that aim to simplify complex tasks. From calculators and graphical user interfaces (GUIs) to new programming languages, these advancements have enabled individuals to create value that was once beyond their reach. The key is to understand that these tools are not meant to replace expertise but to enhance it, making complex tasks more manageable, speeding up development time, and empowering more people.</p>
<!--more-->
<h2 id="what-does-it-mean-to-empower-someone%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#what-does-it-mean-to-empower-someone%3F"></a> What Does It Mean to Empower Someone?</h2>
<p>Often, people perceive "empowerment" as a derogatory term, but it shouldn't be. Empowerment should mean enabling a domain expert to further grow their expertise. They are adding one more tool (and hopefully replacing some) to their toolbox to bring value to what they <strong>know</strong>. And what they know is often hard to explain. It has taken years to master their role, which people usually take for granted because they don't understand the complex dance they do to make their section of the world operate flawlessly.</p>
<h2 id="what-do-they-do%2C-and-why-is-it-hard-to-explain%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#what-do-they-do%2C-and-why-is-it-hard-to-explain%3F"></a> What Do They Do, and Why Is It Hard to Explain?</h2>
<p>Imagine a simple project like building a tire swing. The humorous illustration below shows how different interpretations and outcomes can arise throughout a project lifecycle:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/tire-swing-project-management-flowfuse-node-red-gTCfSVX_vm-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/tire-swing-project-management-flowfuse-node-red-gTCfSVX_vm-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Example of communication issues between the developers and end-users" alt="Tire Swing Analogy" loading="lazy" decoding="async" src="https://flowfuse.com/img/tire-swing-project-management-flowfuse-node-red-gTCfSVX_vm-1920.jpeg" width="1920" height="564" /></picture></p>
<p>This analogy highlights the potential misunderstandings and miscommunications that occur between various stakeholders in a project. From the initial customer explanation to the final product, each stage can introduce variations that stray from the original intent. This leads to a final outcome that often doesn't meet the customer's needs or expectations.</p>
<p>In a perfect world, with clear instructions and years of prep work to define the problem statement, there's a chance the project will be completed close to 95% of what the domain expert wanted, if you're lucky. But how do you close the last 5%? Developers might move on to new projects, or worse, leave the company. The remaining tasks become difficult to complete and often go undone.</p>
<h2 id="put-the-domain-expert-in-the-driver%E2%80%99s-seat" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#put-the-domain-expert-in-the-driver%E2%80%99s-seat"></a> Put the Domain Expert in the Driver’s Seat</h2>
<p>Calculators were not created to be used only by mathematicians. GUIs weren't created to be used only by programmers. They were created to make people's lives easier. No longer was there a <strong>need</strong> to use the command line to start an application; simply click the icon for the desired application. These simple iterations open the opportunity for a larger range of people to leverage tools.</p>
<p>With low-code applications, the process is the same. Put the domain expert in the driver’s seat. Make them their own developer. They understand the process better than anyone else. Pair them with a low-code domain expert and have them co-develop the application.</p>
<p>With the domain expert in the driver’s seat of their own low-code project, the days of the last 5% undone become less common. There is always a path forward.</p>
<h2 id="which-low-code-solution-should-you-go-with%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#which-low-code-solution-should-you-go-with%3F"></a> Which Low-Code Solution Should You Go With?</h2>
<p>With over 10 years of development emerging from <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/history-of-nodered/">IBM’s Emerging Technology Group</a>, Node-RED is a clear leader. Dare I say the de facto solution. It is open-source and easy to spin up. It is currently being leveraged in many industries; ranging from pharmaceuticals, to agriculture, and to telecom.</p>
<p>But you might think Node-RED is just a proof-of-concept tool. You’re wrong. Has it been used as <a href="https://www.cloudflare.com/learning/access-management/what-is-shadow-it/">Shadow-IT</a>? Yes, and that is beautiful and awesome. Innovation is at the heart of Shadow-IT. No one ever said, “There is a tool supported by my IT company that would be perfect for the job, but I think I would rather deploy an unsanctioned tool.” The flaw in this statement is believing there is a choice in tools. Node-RED has often been the only tool available in the domain expert's toolbox.</p>
<h2 id="increased-development-speed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#increased-development-speed"></a> Increased Development Speed</h2>
<p>Domain experts often find themselves needing to develop solutions but learning a new programming language can be time-consuming. With the open-source community behind Node-RED, many common integrations and transformations have already been created. This community sharing of knowledge accelerates development because often a needed programming task has already been developed and shared. This allows domain experts to repurpose existing code as <a href="https://flows.nodered.org/"><strong>nodes, flows, or collections</strong></a>, significantly speeding up the development process.</p>
<h2 id="flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#flowfuse"></a> FlowFuse</h2>
<p>FlowFuse is your solution to solving Shadow-IT and paving the way for your <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/">Citizen Development</a> journey. All the reasons why Node-RED hasn’t been sanctioned within organizations have nothing to do with its ability to execute but more with its lack of scalability, easy-to-use security, and manageability. Enter FlowFuse, developed by the same people who developed your go-to low-code solution. FlowFuse addresses exactly the problems that put it in the Shadow-IT realm.</p>
<p>With security and scalability top of mind, FlowFuse allows IT teams to manage their Node-RED deployments with features like Role-Based Access Control (RBAC), remote management of Node-RED runtimes, and backup and recovery of Node-RED deployments called Snapshots. These are just a few of the features that elevate an already widely used solution, making it ready for enterprise networks.</p>
<h3 id="low-code-platform-success-in-the-real-world" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#low-code-platform-success-in-the-real-world"></a> Low-code platform success in the real world</h3>
<p>Low-code platforms are industry-agnostic, with applications varying from industrial manufacturing to news agencies. Here are some customer success stories of how the FlowFuse low-code platform has helped:</p>
<ul>
<li>
<p><a href="https://flowfuse.com/customer-stories/leveraging-node-red-and-flowfuse-to-automate-precision-manufacturing/"><strong>Abrasive Technology:</strong></a> Leveraged Node-RED and FlowFuse to optimize automation and improve operational efficiency. The transition to FlowFuse Cloud has enhanced their ability to manage Node-RED instances effectively, supporting innovation and agility in their manufacturing processes.</p>
</li>
<li>
<p><a href="https://flowfuse.com/customer-stories/node-red-building-management/"><strong>Power Workplace:</strong></a> Utilized FlowFuse and Node-RED to automate building management, significantly reducing development time and enhancing scalability and reliability. This has helped them meet security and compliance requirements more easily, improving service delivery across multiple regions.</p>
</li>
<li>
<p><a href="https://flowfuse.com/customer-stories/manufacturing-digital-transformation/"><strong>Large US Manufacturing Company:</strong></a> Employed Node-RED and FlowFuse to drive digital transformation across numerous manufacturing sites. By decentralizing innovation, they have improved data visibility, real-time decision-making, and overall process efficiency, using FlowFuse to manage extensive Node-RED deployments and version control.</p>
</li>
</ul>
<h2 id="the-low-code-future" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/why-you-need-a-low-code-platform/#the-low-code-future"></a> The Low-Code Future</h2>
<p>In conclusion, low-code platforms like Node-RED and FlowFuse empower domain experts to take control of their own digital transformation journeys. By putting the domain expert in the driver’s seat, low-code applications enable them to create solutions tailored to their specific needs and requirements. This approach not only reduces the risk of the last 5% of a project going undone but also fosters a culture of innovation and collaboration within organizations.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/Product Mission Statement and Strategy UpdatesWe've recently refined our product strategy to better align with our mission and vision. Here's a summary of the changes.2024-05-14T00:00:00ZJoe Pavitt<p>It's now been two years since our first ever <code>0.1.0</code> release of FlowFuse. In that time, we've been working hard to build out a platform to help users elevate their Node-RED experience. In that time we've delivered some incredibly valuable features to FlowFuse users such as centralized instance management, remote deployments and version control. We've also learned a lot about our users, their needs, and how we can best serve them, and in a recent reflection on our product strategy decided to refine our product mission statement.</p>
<!--more-->
<h2 id="product-mission-statement" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#product-mission-statement"></a> Product Mission Statement</h2>
<p>FlowFuse is used across a range of industries and use-cases, and in the hundreds of conversations we've had with customers, we've found that in every scenario there is a common pattern. So, to reflect ths, we've created a new Product Mission Statement, to make it clear to existing and prospective users of FlowFuse what it is that we're striving for.</p>
<p>Our new mission statement is as follows:</p>
<blockquote>
<p><strong>Provide the best way to build, manage and deploy Node-RED applications at scale, in reliable and secure production environments.</strong></p>
</blockquote>
<p>We believe that by focusing on this mission, we can better serve our users and provide them with the tools they need to succeed. This mission statement will act as the north star for our product prioritization moving forward, and be our compass to ensure we're staying on track in delivering value to our users.</p>
<h3 id="build" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#build"></a> Build</h3>
<p>FlowFuse strives to be the best place to build Node-RED applications by providing tools to help users build applications with more confidence.</p>
<ul>
<li><strong>Simplified Hosting:</strong> Whether you want cloud-hosted instances, or to run Node-RED on edge devices, FlowFuse provides the smoothest experience to setup new Node-RED instances.</li>
<li><strong>Trust</strong>: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/">Certified Nodes</a> ensure Node-RED developers can trust the reliability, quality and security of third-party nodes they're using in their applications.</li>
<li><strong>Version Control</strong>: <a href="https://flowfuse.com/docs/user/snapshots/">Snapshots</a> capture point-in-time views of Node-RED flows, environment variables and other configuration settings. We even capture automatic snapshots of your flows every time you deploy, so you can always be confident you have a backup.</li>
<li><strong>Collaborative Development</strong>: We are building FlowFuse to be a platform that enables teams to work seamlessly together on Node-RED applications. From the <a href="https://flowfuse.com/docs/user/shared-library/">Team Library</a>, through to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-multiplayer/">Node-RED Multiplayer</a>, we want to make sure that FlowFuse is the best platform for collaborative development in Node-RED.</li>
<li><strong>Remote Access:</strong> FlowFuse provides a secure way to access your Node-RED instances and devices from anywhere in the world, so you can build and debug your applications with ease.</li>
</ul>
<h3 id="manage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#manage"></a> Manage</h3>
<p>With FlowFuse, it's possible to manage thousands of Node-RED instances, all in one place. We make sure you have a clear picture of what's happening across all of your instances, and provide tools to help you manage them effectively.</p>
<ul>
<li><strong>Centralized Management</strong>: FlowFuse provides a single entry point to manage all of your Node-RED instances, no matter where they are hosted.</li>
<li><strong>Security</strong>: FlowFuse provides a range of security features to help you keep your Node-RED instances secure. From <a href="https://flowfuse.com/docs/user/user-settings/#two-factor-authentication">Two-Factor Authentication</a> to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">Role-Based Access Control</a>, we're building FlowFuse to be the most secure way to manage your Node-RED instances.</li>
<li><strong>Frictionless Maintenance:</strong> Whether you're updating your Node-RED version, or one of the many third-party dependencies that your applications depend upon, FlowFuse provides tools to make it easy to keep your instances up-to-date and running smoothly.</li>
<li><strong>Observability:</strong> FlowFuse provides tools to help you keep an eye on your Node-RED instances and make sure they're running smoothly. Get alerts when something goes wrong, and use the <a href="https://flowfuse.com/docs/user/logs/#audit-log">Audit Logs</a> to help you debug issues when they arise.</li>
</ul>
<h3 id="deploy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#deploy"></a> Deploy</h3>
<p>With FlowFuse, we aim to provide the best way to deploy Node-RED applications to production environments, whether that be to hundreds of remote devices, or via staging environments to ensure you can deploy with confidence.</p>
<ul>
<li><strong>DevOps:</strong> Test your applications in development and staging environments, ensuring you can deploy them with confidence to production.</li>
<li><strong>Remote Deployments:</strong> Deploy your applications to hundreds of remote devices with a single-click, ensuring you can get your applications running in production quickly and easily.</li>
<li><strong>Reliability:</strong> FlowFuse users can have trust that their applications will run without issues with features like <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/">High Availability</a>. FlowFuse makes sure users have the tools to monitor and manage the reliability of their applications, including being alerted when things go wrong, and making the recovery as easy as possible.</li>
</ul>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#what's-next%3F"></a> What's Next?</h3>
<p>Our development team are busy working on a range of new features to help us deliver on our mission. If you're interested to see what highlight features we're working on for the next release (v2.5) in four weeks time, you can checkout our <a href="https://github.com/orgs/FlowFuse/projects/1/views/39">Development Board</a> on GitHub.</p>
<p>For longer term planning, we have our <a href="https://github.com/orgs/FlowFuse/projects/3/views/9">Product Planning Board</a>, which outlines the features we're working on for the next 12 months. It's subject to change, as we're always <a href="https://flowfuse.com/handbook/company/values/#%F0%9F%94%81-iterative-improvement">iterating</a>, but it gives a good idea of where our focus will likely be in the coming months.</p>
<p>In the mean time, please do reach out if there are any features you'd like to see in FlowFuse, or if you have any feedback on our product strategy. We're always keen to hear from our users and learn how we can better serve you.</p>
<h3 id="read-more" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/product-strategy-updates/#read-more"></a> Read More</h3>
<p>If you're interested in learning more about our company and product strategies, you can read more detail in our Handbook:</p>
<ul>
<li><a href="https://flowfuse.com/handbook/company/strategy/">Company Strategy</a></li>
<li><a href="https://flowfuse.com/handbook/product/strategy/">Product Mission Statement</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/The MIND stack with Node-RED and FlowFuse Dashboard 2.0Leveraging FlowFuse for a Lighter MIND Stack (MIND = MQTT, InfluxDB, Node-RED, Dashboard 2.0)2024-05-14T00:00:00Z<p>The <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/ming-blog/">MING stack</a> has gained significant popularity over the years as it built upon open-source projects. That has given way to many people leveraging this stack to build solutions upon in various different environments. The MING stack is composed of 4 main components, <a href="https://mosquitto.org/">MQTT</a>, <a href="https://www.influxdata.com/">InfluxDB</a>, <a href="https://flowfuse.com/node-red/">Node-RED</a>, and <a href="https://grafana.com/">Grafana</a>. Combined together are the 4 main pillars, data transportation, data storage, data transformation, and visualizations. With this, it requires the management of 4 different applications, which often reside on the same server, but not necessarily. With more moving parts, creates complexity.</p>
<!--more-->
<h2 id="flowfuse%3A-integrated-iot-deployment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#flowfuse%3A-integrated-iot-deployment"></a> FlowFuse: Integrated IoT Deployment</h2>
<p>With FlowFuse we have bundled up 3 of those 4. First and foremost, the FlowFuse platform deploys Node-RED in two main ways. A cloud based deployment and remote deployment with management from the FlowFuse platform. Part of this management piece leverage MQTT between the devices for management. Because of this we have a built in MQTT broker that allows for communication between Node-RED instance within the FlowFuse platform. Lastly, with the release of Dashboards 2.0 from FlowFuse we have introduced the next generation of visualizations for the Node-RED platform.</p>
<p>It often makes sense to deploy a full MING stack, but in some deployments, it might be necessary to deploy a more simplistic version of the MING stack. This is where MIND comes into play.</p>
<h3 id="flowfuse-mqtt%3A-simplified-communications" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#flowfuse-mqtt%3A-simplified-communications"></a> FlowFuse MQTT: Simplified Communications</h3>
<p>FlowFuse has a built-in feature called <a href="https://flowfuse.com/docs/user/projectnodes/">project link nodes</a>, which leverages MQTT, that allows the communication of data between FlowFuse runtimes of Node-RED. One caveat is that this MQTT broker is only available within the FlowFuse platform. What this means is there needs to be some form of translation to be done within Node-RED. This isn’t a big deal for small deployments, because often Node-RED runtimes at the edger are collecting data from various sources that aren’t MQTT. The flow of data is as follows:</p>
<p>Sensor > Node-RED(<a href="https://flowfuse.com/product/device-agent/">FlowFuse Device Agent</a>) > <a href="https://flowfuse.com/docs/user/projectnodes/">MQTT Encapsulated by FlowFuse</a> > Node-RED(FlowFuse Platform) > InfluxDB</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sensor-data-mqtt-node-red-dashboard-influxdb-C_RhRpco9v-1654.avif 1654w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sensor-data-mqtt-node-red-dashboard-influxdb-C_RhRpco9v-1654.webp 1654w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt=""Screenshot showing the flow of data: Sensor > Node-RED(FlowFuse Device Agent) > MQTT Encapsulated by FlowFuse > Node-RED(FlowFuse Platform) > InfluxDB"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sensor-data-mqtt-node-red-dashboard-influxdb-C_RhRpco9v-1654.jpeg" width="1654" height="593" /></picture></p>
<p>A key benefit of having the MQTT broker encapsulated by FlowFuse is that it becomes extremely easy to set up. With the project nodes, there isn’t a need to configure certificates or security credentials because it is only exposed internally to the FlowFuse platform. Furthermore, the access and control limits are applied by the platform so your data remains within the same FlowFuse Team.</p>
<h3 id="influxdb%3A-efficient-data-storage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#influxdb%3A-efficient-data-storage"></a> InfluxDB: Efficient Data Storage</h3>
<p>The deployment of InFluxDB doesn't change in the MIND configuration. The only significant change is that InFluxDB is deployed higher up in the architecture and closer to the FlowFuse Platform instead of the edge.</p>
<h3 id="data-visualization-with-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#data-visualization-with-dashboard-2.0"></a> Data Visualization with Dashboard 2.0</h3>
<p>FlowFuse's Dashboard 2.0 makes it incredibly easy to visualize your data. You can create beautiful and informative dashboards in minutes. Simply drag and drop your desired widgets onto the canvas, and configure them with a few clicks.</p>
<p>We have gone through the step of providing you with an example dashboard for visualizing your MIND stack.</p>
<div id="nr-flow-137" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow137 = "\n[{\"id\":\"dc1c7f4e5ddc86f8\",\"type\":\"tab\",\"label\":\"MIND\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"1423665d39048eba\",\"type\":\"mqtt in\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"\",\"topic\":\"flowfuse/temp\",\"qos\":\"2\",\"datatype\":\"auto-detect\",\"broker\":\"e8ea838e83035408\",\"nl\":false,\"rap\":true,\"rh\":0,\"inputs\":0,\"x\":190,\"y\":160,\"wires\":[[\"7ddcaadd5c43f11d\",\"c359b55bc77165c2\"]]},{\"id\":\"993651c073e7659c\",\"type\":\"mqtt in\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"\",\"topic\":\"flowfuse/RH\",\"qos\":\"2\",\"datatype\":\"auto-detect\",\"broker\":\"e8ea838e83035408\",\"nl\":false,\"rap\":true,\"rh\":0,\"inputs\":0,\"x\":190,\"y\":220,\"wires\":[[\"01f4e507e7deaaea\",\"33e5c2e4a61d01e3\"]]},{\"id\":\"7ddcaadd5c43f11d\",\"type\":\"change\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Temp\",\"rules\":[{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"temp\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":350,\"y\":160,\"wires\":[[\"f39435df2b4db94f\"]]},{\"id\":\"01f4e507e7deaaea\",\"type\":\"change\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"RH\",\"rules\":[{\"t\":\"set\",\"p\":\"topic\",\"pt\":\"msg\",\"to\":\"RH\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":350,\"y\":220,\"wires\":[[\"f39435df2b4db94f\"]]},{\"id\":\"f39435df2b4db94f\",\"type\":\"join\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"\",\"mode\":\"custom\",\"build\":\"object\",\"property\":\"payload\",\"propertyType\":\"msg\",\"key\":\"topic\",\"joiner\":\"\\\\n\",\"joinerType\":\"str\",\"accumulate\":false,\"timeout\":\"\",\"count\":\"2\",\"reduceRight\":false,\"reduceExp\":\"\",\"reduceInit\":\"\",\"reduceInitType\":\"\",\"reduceFixup\":\"\",\"x\":550,\"y\":180,\"wires\":[[\"fa067011fa3f705f\",\"c9d37220d787ac92\",\"681b2e5009ec5e1e\"]]},{\"id\":\"fa067011fa3f705f\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Send it to InfluxDB\",\"func\":\"var temp = Number(msg.payload.temp);\\nvar RH = Number(msg.payload.RH);\\n\\nmsg.measurement = \\\"room1\\\";\\nmsg.payload = {Temperature:temp,RH:RH};\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":770,\"y\":180,\"wires\":[[\"0e86e1bd4bf8b9f6\"]]},{\"id\":\"0e86e1bd4bf8b9f6\",\"type\":\"influxdb out\",\"z\":\"dc1c7f4e5ddc86f8\",\"influxdb\":\"cb09abcd8b693ca8\",\"name\":\"\",\"measurement\":\"\",\"precision\":\"\",\"retentionPolicy\":\"\",\"database\":\"database\",\"precisionV18FluxV20\":\"ms\",\"retentionPolicyV18Flux\":\"\",\"org\":\"organization\",\"bucket\":\"bucket\",\"x\":1080,\"y\":180,\"wires\":[]},{\"id\":\"c359b55bc77165c2\",\"type\":\"ui-text\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"11df00492fb20acd\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Temperature\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Verdana,Verdana,Geneva,sans-serif\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":530,\"y\":100,\"wires\":[]},{\"id\":\"33e5c2e4a61d01e3\",\"type\":\"ui-text\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"11df00492fb20acd\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"RH\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Verdana,Verdana,Geneva,sans-serif\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":510,\"y\":280,\"wires\":[]},{\"id\":\"c9d37220d787ac92\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Temp\",\"func\":\"var temp = Number(msg.payload.temp);\\n\\nmsg.payload = temp;\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":730,\"y\":120,\"wires\":[[\"432850955a867e1e\",\"28d0b846fd2a48ab\"]]},{\"id\":\"681b2e5009ec5e1e\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"RH\",\"func\":\"var RH = Number(msg.payload.RH);\\nmsg.payload = RH;\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":730,\"y\":240,\"wires\":[[\"a41e65a351476137\",\"80b1312f88e4f656\"]]},{\"id\":\"432850955a867e1e\",\"type\":\"ui-chart\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"aa70ec0abc058d78\",\"name\":\"\",\"label\":\"Live Temperature \",\"order\":9007199254740991,\"chartType\":\"line\",\"category\":\"Temperature\",\"categoryType\":\"str\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"time\",\"yAxisProperty\":\"\",\"ymin\":\"0\",\"ymax\":\"50\",\"action\":\"append\",\"pointShape\":\"line\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"width\":\"6\",\"height\":\"8\",\"className\":\"\",\"x\":950,\"y\":120,\"wires\":[[]]},{\"id\":\"a41e65a351476137\",\"type\":\"ui-chart\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"aa70ec0abc058d78\",\"name\":\"\",\"label\":\"Live RH\",\"order\":9007199254740991,\"chartType\":\"line\",\"category\":\"RH\",\"categoryType\":\"str\",\"xAxisProperty\":\"\",\"xAxisPropertyType\":\"msg\",\"xAxisType\":\"time\",\"yAxisProperty\":\"\",\"ymin\":\"0\",\"ymax\":\"100\",\"action\":\"append\",\"pointShape\":\"line\",\"pointRadius\":4,\"showLegend\":true,\"removeOlder\":1,\"removeOlderUnit\":\"3600\",\"removeOlderPoints\":\"\",\"colors\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"width\":\"6\",\"height\":\"8\",\"className\":\"\",\"x\":940,\"y\":240,\"wires\":[[]]},{\"id\":\"87ebfee665b784ee\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Min\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT min(Temperature) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":500,\"wires\":[[\"7d727f5a7723a73d\"]]},{\"id\":\"7d727f5a7723a73d\",\"type\":\"influxdb in\",\"z\":\"dc1c7f4e5ddc86f8\",\"influxdb\":\"cb09abcd8b693ca8\",\"name\":\"Read Temp\",\"query\":\"\",\"rawOutput\":false,\"precision\":\"\",\"retentionPolicy\":\"\",\"org\":\"organization\",\"x\":610,\"y\":540,\"wires\":[[\"fe9230e31884d82b\"]]},{\"id\":\"1da5df166734acac\",\"type\":\"switch\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Temp\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"Min\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"Max\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"Total\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":3,\"x\":310,\"y\":540,\"wires\":[[\"87ebfee665b784ee\"],[\"1322af95dd29b8c3\"],[\"717e1a07222ec348\"]]},{\"id\":\"1322af95dd29b8c3\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Max\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT max(Temperature) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":540,\"wires\":[[\"7d727f5a7723a73d\"]]},{\"id\":\"88e2e00b4e5f5fd6\",\"type\":\"ui-dropdown\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"f02746ad7e1ab68c\",\"name\":\"\",\"label\":\"Select Option:\",\"tooltip\":\"\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"multiple\":false,\"options\":[{\"label\":\"Min\",\"value\":\"Min\",\"type\":\"str\"},{\"label\":\"Max\",\"value\":\"Max\",\"type\":\"str\"},{\"label\":\"Total\",\"value\":\"Total\",\"type\":\"str\"}],\"payload\":\"\",\"topic\":\"topic\",\"topicType\":\"msg\",\"className\":\"\",\"x\":160,\"y\":540,\"wires\":[[\"1da5df166734acac\"]]},{\"id\":\"57a7757bf943bce5\",\"type\":\"ui-text\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"f02746ad7e1ab68c\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Temp Stats\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Verdana,Verdana,Geneva,sans-serif\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1290,\"y\":540,\"wires\":[]},{\"id\":\"cac36785f12f3bf5\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Min\",\"func\":\"var min = msg.payload[0].min\\nmsg.payload = \\\"Minimum is \\\" + min;\\nreturn (msg)\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":500,\"wires\":[[\"57a7757bf943bce5\"]]},{\"id\":\"fc6330b5fcc33acc\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Max\",\"func\":\"var avg = msg.payload[0].max;\\nmsg.payload = \\\"Max is \\\" + avg;\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":540,\"wires\":[[\"57a7757bf943bce5\"]]},{\"id\":\"6453914fa7bdd751\",\"type\":\"switch\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Temp\",\"property\":\"payload[0]\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"hask\",\"v\":\"min\",\"vt\":\"str\"},{\"t\":\"hask\",\"v\":\"max\",\"vt\":\"str\"},{\"t\":\"hask\",\"v\":\"count\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":3,\"x\":950,\"y\":540,\"wires\":[[\"cac36785f12f3bf5\"],[\"fc6330b5fcc33acc\"],[\"d29294d236bef25c\"]]},{\"id\":\"717e1a07222ec348\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Count\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT count(Temperature) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":580,\"wires\":[[\"7d727f5a7723a73d\"]]},{\"id\":\"d29294d236bef25c\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Count\",\"func\":\"var count = msg.payload[0].count;\\nmsg.payload = \\\"Total Record is \\\" + count;\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":580,\"wires\":[[\"57a7757bf943bce5\"]]},{\"id\":\"e7465ddb4fd72e6a\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Min\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT min(RH) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":620,\"wires\":[[\"db0873d1bbef089a\"]]},{\"id\":\"db0873d1bbef089a\",\"type\":\"influxdb in\",\"z\":\"dc1c7f4e5ddc86f8\",\"influxdb\":\"cb09abcd8b693ca8\",\"name\":\"Read RH\",\"query\":\"\",\"rawOutput\":false,\"precision\":\"\",\"retentionPolicy\":\"\",\"org\":\"organization\",\"x\":600,\"y\":660,\"wires\":[[\"f14e43cc318fa92b\"]]},{\"id\":\"a27e50703cb15e38\",\"type\":\"switch\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"RH\",\"property\":\"payload\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"Min\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"Max\",\"vt\":\"str\"},{\"t\":\"eq\",\"v\":\"Total\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":3,\"x\":310,\"y\":660,\"wires\":[[\"e7465ddb4fd72e6a\"],[\"0e8acb8b65e368f6\"],[\"7402a7eec1245643\"]]},{\"id\":\"0e8acb8b65e368f6\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Max\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT max(RH) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":660,\"wires\":[[\"db0873d1bbef089a\"]]},{\"id\":\"a36dafb79f2fce75\",\"type\":\"ui-dropdown\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"f02746ad7e1ab68c\",\"name\":\"\",\"label\":\"Select Option:\",\"tooltip\":\"\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"multiple\":false,\"options\":[{\"label\":\"Min\",\"value\":\"Min\",\"type\":\"str\"},{\"label\":\"Max\",\"value\":\"Max\",\"type\":\"str\"},{\"label\":\"Total\",\"value\":\"Total\",\"type\":\"str\"}],\"payload\":\"\",\"topic\":\"topic\",\"topicType\":\"msg\",\"className\":\"\",\"x\":160,\"y\":660,\"wires\":[[\"a27e50703cb15e38\"]]},{\"id\":\"4800381c4950d245\",\"type\":\"ui-text\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"f02746ad7e1ab68c\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"RH Stats\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":true,\"font\":\"Verdana,Verdana,Geneva,sans-serif\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1280,\"y\":660,\"wires\":[]},{\"id\":\"4af1d2b0abf0009f\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Min\",\"func\":\"var min = msg.payload[0].min\\nmsg.payload = \\\"Minimum is \\\" + min;\\nreturn (msg)\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":620,\"wires\":[[\"4800381c4950d245\"]]},{\"id\":\"cfc4f16f835d3724\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Max\",\"func\":\"var avg = msg.payload[0].max;\\nmsg.payload = \\\"Max is \\\" + avg;\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":660,\"wires\":[[\"4800381c4950d245\"]]},{\"id\":\"49ac1cdfe4289eac\",\"type\":\"switch\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"RH\",\"property\":\"payload[0]\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"hask\",\"v\":\"min\",\"vt\":\"str\"},{\"t\":\"hask\",\"v\":\"max\",\"vt\":\"str\"},{\"t\":\"hask\",\"v\":\"count\",\"vt\":\"str\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":3,\"x\":950,\"y\":660,\"wires\":[[\"4af1d2b0abf0009f\"],[\"cfc4f16f835d3724\"],[\"57b2d15bae6a4d42\"]]},{\"id\":\"7402a7eec1245643\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Count\",\"func\":\"// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT count(RH) FROM room1`;\\n\\n// Pass the modified message object to the next node\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":450,\"y\":700,\"wires\":[[\"db0873d1bbef089a\"]]},{\"id\":\"57b2d15bae6a4d42\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Count\",\"func\":\"var count = msg.payload[0].count;\\nmsg.payload = \\\"Total Record is \\\" + count;\\nreturn msg;\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1090,\"y\":700,\"wires\":[[\"4800381c4950d245\"]]},{\"id\":\"fb1cbe8ec45b5de7\",\"type\":\"comment\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Write Operation\",\"info\":\"\",\"x\":100,\"y\":80,\"wires\":[]},{\"id\":\"931c16333d3efcad\",\"type\":\"comment\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Read Operation\",\"info\":\"\",\"x\":180,\"y\":340,\"wires\":[]},{\"id\":\"f200e4be2158481e\",\"type\":\"comment\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Read Operation\",\"info\":\"\",\"x\":180,\"y\":480,\"wires\":[]},{\"id\":\"80b1312f88e4f656\",\"type\":\"ui-gauge\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Live RH\",\"group\":\"f02746ad7e1ab68c\",\"order\":0,\"width\":\"0\",\"height\":\"0\",\"gtype\":\"gauge-34\",\"gstyle\":\"rounded\",\"title\":\"Live RH\",\"units\":\"%\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":[{\"from\":\"0\",\"color\":\"#5cd65c\"},{\"from\":\"40\",\"color\":\"#ffc800\"},{\"from\":\"70\",\"color\":\"#ea5353\"}],\"min\":0,\"max\":\"100\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":940,\"y\":280,\"wires\":[]},{\"id\":\"28d0b846fd2a48ab\",\"type\":\"ui-gauge\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Live Temperature\",\"group\":\"f02746ad7e1ab68c\",\"order\":0,\"width\":\"0\",\"height\":\"0\",\"gtype\":\"gauge-34\",\"gstyle\":\"rounded\",\"title\":\"Temperature\",\"units\":\"Degrees C\",\"icon\":\"\",\"prefix\":\"\",\"suffix\":\"\",\"segments\":[{\"from\":\"0\",\"color\":\"#5cd65c\"},{\"from\":\"30\",\"color\":\"#ffc800\"},{\"from\":\"40\",\"color\":\"#ea5353\"}],\"min\":0,\"max\":\"50\",\"sizeThickness\":16,\"sizeGap\":4,\"sizeKeyThickness\":8,\"styleRounded\":true,\"styleGlow\":false,\"className\":\"\",\"x\":950,\"y\":80,\"wires\":[]},{\"id\":\"f22ff292ec2d0964\",\"type\":\"ui-form\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Start Time\",\"group\":\"11df00492fb20acd\",\"label\":\"Select Date and Time\",\"order\":0,\"width\":0,\"height\":0,\"options\":[{\"label\":\"Start\",\"key\":\"Start Date\",\"type\":\"date\",\"required\":true,\"rows\":null},{\"label\":\"Start Time\",\"key\":\"Start Time\",\"type\":\"time\",\"required\":true,\"rows\":null},{\"label\":\"Stop Date\",\"key\":\"Stop Date\",\"type\":\"date\",\"required\":true,\"rows\":null},{\"label\":\"Stop Time\",\"key\":\"Stop Time\",\"type\":\"time\",\"required\":true,\"rows\":null}],\"formValue\":{\"Start Date\":\"\",\"Start Time\":\"\",\"Stop Date\":\"\",\"Stop Time\":\"\"},\"payload\":\"\",\"submit\":\"submit\",\"cancel\":\"clear\",\"resetOnSubmit\":false,\"topic\":\"topic\",\"topicType\":\"msg\",\"splitLayout\":false,\"className\":\"\",\"x\":160,\"y\":420,\"wires\":[[\"05dfd0f5d8b97fba\"]]},{\"id\":\"05dfd0f5d8b97fba\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"start date and end date\",\"func\":\"var startDate = msg.payload[\\\"Start Date\\\"];\\nvar startTime = msg.payload[\\\"Start Time\\\"];\\nvar stopDate = msg.payload[\\\"Stop Date\\\"];\\nvar stopTime = msg.payload[\\\"Stop Time\\\"];\\n\\n\\n\\nvar startTimestamp = `${startDate}T${startTime}:00+05:30`; // +05:30 is the offset for IST\\nvar stopTimestamp = `${stopDate}T${stopTime}:00+05:30`; // +05:30 is the offset for IST\\n\\n// Construct the query using the provided start and stop timestamps\\nmsg.query = `SELECT * FROM room1 WHERE time >= '${startTimestamp}' AND time <= '${stopTimestamp}'`;\\n\\nreturn msg;\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":410,\"y\":420,\"wires\":[[\"a1d3dda63ad3bd6f\"]]},{\"id\":\"a1d3dda63ad3bd6f\",\"type\":\"influxdb in\",\"z\":\"dc1c7f4e5ddc86f8\",\"influxdb\":\"cb09abcd8b693ca8\",\"name\":\"Read\",\"query\":\"\",\"rawOutput\":false,\"precision\":\"\",\"retentionPolicy\":\"\",\"org\":\"organization\",\"x\":610,\"y\":420,\"wires\":[[\"ad4f278b680749b2\"]]},{\"id\":\"024b2830c4183fd1\",\"type\":\"function\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Table\",\"func\":\"var temp=[];\\nvar a = msg.payload;\\n\\na.forEach(function (value,index) {\\ntemp.push(value)\\n});\\n\\nmsg.payload=temp\\n\\nreturn msg;\",\"outputs\":1,\"timeout\":\"\",\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":[],\"x\":1650,\"y\":780,\"wires\":[[]]},{\"id\":\"5bb2164a578330bd\",\"type\":\"ui-table\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"11df00492fb20acd\",\"name\":\"Historic Data\",\"label\":\"text\",\"order\":0,\"width\":0,\"height\":0,\"maxrows\":0,\"autocols\":true,\"columns\":[],\"x\":1050,\"y\":420,\"wires\":[]},{\"id\":\"ad4f278b680749b2\",\"type\":\"copy-array\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Table\",\"x\":790,\"y\":420,\"wires\":[[\"5bb2164a578330bd\"]]},{\"id\":\"dd00c6e47e1480ad\",\"type\":\"ui-button\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"aa70ec0abc058d78\",\"name\":\"\",\"label\":\"Clear Temp\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"[]\",\"payloadType\":\"json\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":730,\"y\":60,\"wires\":[[\"432850955a867e1e\"]]},{\"id\":\"7e6d8eb54dc0d8aa\",\"type\":\"ui-button\",\"z\":\"dc1c7f4e5ddc86f8\",\"group\":\"aa70ec0abc058d78\",\"name\":\"\",\"label\":\"Clear RH\",\"order\":0,\"width\":0,\"height\":0,\"passthru\":false,\"tooltip\":\"\",\"color\":\"\",\"bgcolor\":\"\",\"className\":\"\",\"icon\":\"\",\"payload\":\"[]\",\"payloadType\":\"json\",\"topic\":\"topic\",\"topicType\":\"msg\",\"x\":740,\"y\":300,\"wires\":[[\"a41e65a351476137\"]]},{\"id\":\"fe9230e31884d82b\",\"type\":\"copy-array\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"Temp Data\",\"x\":810,\"y\":540,\"wires\":[[\"6453914fa7bdd751\"]]},{\"id\":\"f14e43cc318fa92b\",\"type\":\"copy-array\",\"z\":\"dc1c7f4e5ddc86f8\",\"name\":\"RH Data\",\"x\":800,\"y\":660,\"wires\":[[\"49ac1cdfe4289eac\"]]},{\"id\":\"e8ea838e83035408\",\"type\":\"mqtt-broker\",\"name\":\"\",\"broker\":\"192.168.1.39\",\"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\":\"cb09abcd8b693ca8\",\"type\":\"influxdb\",\"hostname\":\"127.0.0.1\",\"port\":\"8086\",\"protocol\":\"http\",\"database\":\"environment\",\"name\":\"\",\"usetls\":false,\"tls\":\"cff98b62.009678\",\"influxdbVersion\":\"1.x\",\"url\":\"http://localhost:8086\",\"timeout\":\"10\",\"rejectUnauthorized\":true},{\"id\":\"11df00492fb20acd\",\"type\":\"ui-group\",\"name\":\"Room 1\",\"page\":\"6a43b6805a66d2b8\",\"width\":\"6\",\"height\":\"1\",\"order\":-1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"aa70ec0abc058d78\",\"type\":\"ui-group\",\"name\":\"Live Data\",\"page\":\"6a43b6805a66d2b8\",\"width\":\"6\",\"height\":\"1\",\"order\":-1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"f02746ad7e1ab68c\",\"type\":\"ui-group\",\"name\":\"Stat Data\",\"page\":\"6a43b6805a66d2b8\",\"width\":\"6\",\"height\":\"1\",\"order\":-1,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"cff98b62.009678\",\"type\":\"tls-config\",\"name\":\"\",\"cert\":\"\",\"key\":\"\",\"ca\":\"\",\"certname\":\"\",\"keyname\":\"\",\"caname\":\"\",\"servername\":\"\",\"verifyservercert\":false},{\"id\":\"6a43b6805a66d2b8\",\"type\":\"ui-page\",\"name\":\"Environment Data\",\"ui\":\"20ae73d735a60fbc\",\"path\":\"/page2\",\"icon\":\"home\",\"layout\":\"flex\",\"theme\":\"074b315414230834\",\"order\":-1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"20ae73d735a60fbc\",\"type\":\"ui-base\",\"name\":\"UI Name\",\"path\":\"/dashboard\",\"showPathInSidebar\":false},{\"id\":\"074b315414230834\",\"type\":\"ui-theme\",\"name\":\"Theme Name\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"},\"sizes\":{\"pagePadding\":\"12px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"4px\",\"widgetGap\":\"12px\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow137.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-137') })</script>
<p>This dashboard example allows you to do quick visualizations of data from an InfluxDB. In this particular case, it leverages the Relative Humidity (RH) and Temperature of a sensor. It captures the data and stores in InfluxDB and then visualizes it in Dashboard 2.0. Use this as a starting point to display data in the MIND stack.</p>
<h2 id="advantages-of-flowfuse%E2%80%99s-mind-stack" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#advantages-of-flowfuse%E2%80%99s-mind-stack"></a> Advantages of FlowFuse’s MIND Stack</h2>
<p>FlowFuse provides built-in features, such as MQTT, backups, deployment pipelines, and access control, helping to protect data and devices from unauthorized access. FlowFuse simplifies the MING stack to the MIND deployment, bringing numerous benefits to organizations. It reduces the complexity of managing multiple components, minimizing configuration, update, and maintenance overhead. The single platform approach for MQTT, Node-RED, and visualization simplifies maintenance tasks, lowers the risk of conflicts, and ensures consistency. FlowFuse's lighter deployment requires fewer hardware resources, making it suitable for resource-constrained environments or cost-sensitive projects. Organizations can potentially save on licensing and maintenance costs by reducing the number of deployed software components. Faster deployment is enabled with fewer components to set up and configure, allowing projects to realize value sooner. FlowFuse's scalable architecture handles large data volumes and devices, ensuring suitability for growing IoT deployments.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/#conclusion"></a> Conclusion</h2>
<p>While we believe that the MING stack is here to stay, we believe as the market matures, it should offer many different variations suitable for each customer's needs. However, we find that the MIND offering does fill a niche in the market that may better suit your needs. None the less, choosing FlowFuse to manage your Node-RED runtimes will ensure that your applications will be secure, scalable, and easily manageable. Allowing your domain experts to take control and extend their knowledge bringing increased value to industrial facilities around the world.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=The%20MIND%20stack%20with%20Node-RED%20and%20FlowFuse%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/Mapping location data within Node-RED Dashboard 2.0.Step-by-step guide to plot location data on dashboard 2.0.2024-05-13T00:00:00ZSumit Shinde<p>Fleet management in IoT uses sensors and software to collect real-time data on vehicles, such as location, fuel consumption, and driver behavior. This data allows companies to optimize routes, reduce costs, improve safety, and enhance overall operational efficiency of their fleet. Building an application that allows the tracking of location to support Fleet management is what this post is about.</p>
<!--more-->
<p>Before moving further, make sure you have installed Dashboard 2.0. If you are new to Dashboard 2.0, please refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started with Dashboard 2.0</a> for more information.</p>
<h2 id="installing-world-map-custom-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#installing-world-map-custom-node"></a> Installing world map custom node</h2>
<p>To render an interactive world map webpage for plotting location data, we will use a popular custom node called <a href="https://flows.nodered.org/node/node-red-contrib-web-worldmap">node-red-contrib-web-worldmap-page</a>. This node offers extensive features enabling us to render a world map and plot various items with different icons, colors, NATO symbologies, ranges, and more.</p>
<ol>
<li>Click the Node-RED Settings (top-right).</li>
<li>Click "Manage Palette".</li>
<li>Switch to the "Install" tab.</li>
<li>Search for <code>node-red-contrib-web-worldmap</code>.</li>
<li>Click "Install".</li>
</ol>
<h2 id="retrieving-location-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#retrieving-location-data"></a> Retrieving location Data</h2>
<p>Before plotting locations, we need to obtain the data first. For this purpose, we will utilize the <a href="https://tfe-opendata.readme.io/docs/getting-started">Edenburg Open Public Transportation API</a>. This API provides the live locations of all of Edenburg's public buses and trams, enabling us to access the necessary data for plotting on Worldmap within Dashboard 2.0.</p>
<ol>
<li>Drag an <strong>Inject</strong> node onto the canvas and set the repeat property to a 20-second interval.</li>
<li>Drag an HTTP request node onto the canvas, Dobule-click on it and choose <strong>GET</strong> method, and enter <code>https://tfe-opendata.com/api/v1/vehicle_locations</code> in the URL field and select return as <strong>a parsed json object</strong>. This API is public, so no need for environment variables. For private APIs, consider using <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">environment variables</a> for security.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-http-request-node-1IfEbx1317-531.avif 531w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-http-request-node-1IfEbx1317-531.webp 531w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the HTTP request node configuration for retrieving data from the API" alt=""Screenshot of the HTTP request node configuration for retrieving data from the API"" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-http-request-node-1IfEbx1317-531.jpeg" width="531" height="746" /></picture></p>
<ol start="3">
<li>Connect the <strong>Inject</strong> node's output to the <strong>HTTP request</strong> node's input.</li>
</ol>
<h2 id="formatting-location-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#formatting-location-data"></a> Formatting Location Data</h2>
<p>To ensure compatibility with the Worldmap custom node, we need to format the location data appropriately.</p>
<ol>
<li>Drag the <strong>Change</strong> node onto the canvas, and set the <code>msg.payload</code> to <code>msg.payload.vehicles</code>, and give it name <strong>Set payload</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-jKrTYnDJbB-517.avif 517w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-jKrTYnDJbB-517.webp 517w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Change node setting the payload to the vehicles JSON array containing actual vehicle location data" alt="Screenshot of the Change node setting the payload to the vehicles JSON array containing actual vehicle location data" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-jKrTYnDJbB-517.jpeg" width="517" height="557" /></picture></p>
<ol start="2">
<li>Drag a <strong>Split</strong> node onto the canvas.</li>
<li>Add another <strong>Change</strong> node to the canvas. Configure it to set and delete properties as shown in the image below, and give it name <strong>Change and delete properties</strong>. By changing and deleting properties of the location, we ensure that only the properties acceptable by the <strong>Worldmap</strong> node are included in the location data.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-changing-and-deleting-properties-oSgCUYNBu4-402.avif 402w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-changing-and-deleting-properties-oSgCUYNBu4-402.webp 402w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Change node configuring properties to ensure compatibility with the Worldmap node" alt="Screenshot of the Change node configuring properties to ensure compatibility with the Worldmap node" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-changing-and-deleting-properties-oSgCUYNBu4-402.jpeg" width="402" height="645" /></picture></p>
<ol start="4">
<li>Drag a <strong>Switch</strong> node onto the canvas and add conditions to check if <code>msg.payload.vehicle_type</code> is equal to <strong>tram</strong> or not.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-switch-node-checking-is-vehicale-type-is-tram-or-not-AIJnR0fwQz-389.avif 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-switch-node-checking-is-vehicale-type-is-tram-or-not-AIJnR0fwQz-389.webp 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Switch node checking if the vehicle type is tram or not" alt="Screenshot of the Switch node checking if the vehicle type is tram or not" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-switch-node-checking-is-vehicale-type-is-tram-or-not-AIJnR0fwQz-389.jpeg" width="389" height="343" /></picture></p>
<ol start="5">
<li>Add another <strong>Change</strong> node to the canvas and give it a name <strong>set icon and icon color for tram</strong>. Set <code>msg.payload.icon</code> to <strong>fa-train</strong> and <code>msg.payload.iconColor</code> to <strong>yellow</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-tram-oaaZso9qHW-406.avif 406w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-tram-oaaZso9qHW-406.webp 406w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Change node setting the icon and icon color for tram" alt="Screenshot of the Change node setting the icon and icon color for tram" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-tram-oaaZso9qHW-406.jpeg" width="406" height="346" /></picture></p>
<ol start="7">
<li>Add another <strong>Change</strong> node to the canvas and give it a name <strong>set icon and icon color for bus</strong>. Set <code>msg.payload.icon</code> to <strong>bus</strong> and <code>msg.payload.iconColor</code> to <strong>red</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-bus-2KHhYq1jap-410.avif 410w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-bus-2KHhYq1jap-410.webp 410w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the Change node setting the icon and icon color for bus" alt="Screenshot of the Change node setting the icon and icon color for bus" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-change-node-setting-icon-for-bus-2KHhYq1jap-410.jpeg" width="410" height="362" /></picture></p>
<ol start="8">
<li>Connect the <strong>HTTP request</strong> node's output to the input of the <strong>Change</strong> node named <strong>Set payload</strong>, and connect the output of that <strong>Change</strong> node to the input of the <strong>Split</strong> node.</li>
<li>Then, connect the output of the <strong>Split</strong> node to the input of the <strong>Change</strong> node named <strong>Change and delete properties</strong>, and connect the output of the "Change and delete properties" node to the input of the <strong>Switch</strong> node. Then <strong>Switch</strong> node's first output to the input of the <strong>Change</strong> node named <strong>set icon and icon color for tram</strong>, and its second output to the input of the <strong>Change</strong> node named <strong>set icon and icon color for bus</strong>.</li>
</ol>
<h2 id="plotting-location-data-on-worldmap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#plotting-location-data-on-worldmap"></a> Plotting location data on Worldmap</h2>
<ol>
<li>Drag a <strong>Worldmap</strong> node onto the canvas. Set the path to <strong>/worldmap</strong> and keep the rest of the settings unchanged, although you can modify other properties according to your preferences.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-worldmap-node-2sdBMvlFDq-530.avif 530w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-worldmap-node-2sdBMvlFDq-530.webp 530w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the configuration of the Worldmap custom node" alt=""Screenshot displaying the configuration of the Worldmap custom node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-worldmap-node-2sdBMvlFDq-530.jpeg" width="530" height="782" /></picture></p>
<ol start="2">
<li>With the <strong>worldmap</strong> node configured, it will generate a world map with plotted data accessible at the specified path.</li>
<li>Connect the <strong>Function</strong> node's output to <strong>Worldmap</strong> node's input.</li>
</ol>
<p>Now we have successfully created a page with a world map displaying plotted vehicle location data. To confirm, you can visit <code>https://<your-instance-name>.flowfuse.cloud/worldmap</code>.</p>
<h2 id="rendering-map-on-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#rendering-map-on-dashboard-2.0"></a> Rendering map on Dashboard 2.0</h2>
<p>To render worlmap webpage on dashboard 2.0 we will use <strong>iframe</strong> custom widget which will allow us to embed an external webpage into Dashboard 2.0 applications using an iframe.</p>
<h3 id="installing-iframe-custom-widget" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#installing-iframe-custom-widget"></a> Installing iframe custom widget</h3>
<ol>
<li>Click the Node-RED Settings (top-right)</li>
<li>Click <strong>Manage Palette</strong></li>
<li>Switch to the <strong>Install</strong> tab</li>
<li>Search for <code>@flowfuse/node-red-dashboard-2-ui-iframe</code></li>
<li>Click <strong>Install</strong></li>
</ol>
<h3 id="rendering-worlmap-on-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#rendering-worlmap-on-dashboard-2.0"></a> Rendering worlmap on Dashboard 2.0</h3>
<ol>
<li>Drag an <strong>iframe</strong> widget onto the canvas.</li>
<li>Select <strong>ui-group</strong> and <strong>ui-base</strong> for it, where you want to render the webpage.</li>
<li>Set height and width for it according to your preference and set URL to <strong>/worlmap</strong>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-iframe-widget-udGbOyPGR--571.avif 571w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-iframe-widget-udGbOyPGR--571.webp 571w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing configurations of iframe widget for rendering worlmap page on dashboard" alt=""Screenshot showing configurations of iframe widget for rendering worlmap page on dashboard"" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-iframe-widget-udGbOyPGR--571.jpeg" width="571" height="580" /></picture></p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#deploying-the-flow"></a> Deploying the flow</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/mapping-location-on-dashboard-2-uk-live-transport-erEtRoMwj8-853.gif 853w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image displaying live locations of UK public transport on the dashboard" alt=""Image displaying live locations of UK public transport on the dashboard"" loading="lazy" decoding="async" src="https://flowfuse.com/img/mapping-location-on-dashboard-2-uk-live-transport-erEtRoMwj8-853.webp" width="853" height="480" /></picture></p>
<div id="nr-flow-136" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow136 = "\n[{\"id\":\"4e45e8ef870b86fb\",\"type\":\"group\",\"z\":\"eacc68e72f120b0e\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":[\"b6917d83.d1bac\",\"3842171.4d487e8\",\"b4f2e2dabd5b8220\",\"5f0d149d4dc38916\",\"2ff7e9501ad50cd5\",\"a08f0a836ac412a7\",\"bc891dc2aaaedc39\",\"726a8da3fe930e54\",\"b3dd7814ea5270ca\",\"0c5d6bcfd71d40da\",\"e037ee3d1f702d25\",\"d7ca6c3cdf176e4e\"],\"x\":814,\"y\":759,\"w\":1872,\"h\":322},{\"id\":\"b6917d83.d1bac\",\"type\":\"http request\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"\",\"method\":\"GET\",\"ret\":\"obj\",\"paytoqs\":\"ignore\",\"url\":\"https://tfe-opendata.com/api/v1/vehicle_locations\",\"tls\":\"\",\"persist\":false,\"proxy\":\"\",\"insecureHTTPParser\":false,\"authType\":\"\",\"senderr\":false,\"headers\":[],\"x\":1190,\"y\":900,\"wires\":[[\"a08f0a836ac412a7\"]]},{\"id\":\"3842171.4d487e8\",\"type\":\"inject\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"get transporatation data\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"5\",\"crontab\":\"\",\"once\":false,\"onceDelay\":\"\",\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"str\",\"x\":970,\"y\":900,\"wires\":[[\"b6917d83.d1bac\"]]},{\"id\":\"b4f2e2dabd5b8220\",\"type\":\"worldmap\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"worldmap\",\"lat\":\"\",\"lon\":\"\",\"zoom\":\"\",\"layer\":\"OSMG\",\"cluster\":\"\",\"maxage\":\"\",\"usermenu\":\"show\",\"layers\":\"show\",\"panit\":\"false\",\"panlock\":\"false\",\"zoomlock\":\"false\",\"hiderightclick\":\"false\",\"coords\":\"mgrs\",\"showgrid\":\"false\",\"showruler\":\"false\",\"allowFileDrop\":\"false\",\"path\":\"/worldmap\",\"overlist\":\"DR,CO,RA,DN\",\"maplist\":\"OSMG,OSMC,EsriC,EsriS,UKOS\",\"mapname\":\"\",\"mapurl\":\"\",\"mapopt\":\"\",\"mapwms\":false,\"x\":2600,\"y\":900,\"wires\":[]},{\"id\":\"5f0d149d4dc38916\",\"type\":\"comment\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"Retrieving, formatting, and plotting location data on a world map.\",\"info\":\"\",\"x\":1390,\"y\":800,\"wires\":[]},{\"id\":\"2ff7e9501ad50cd5\",\"type\":\"comment\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"Rendering a map with plotted data on Dashboard 2.0.\",\"info\":\"\",\"x\":1440,\"y\":980,\"wires\":[]},{\"id\":\"a08f0a836ac412a7\",\"type\":\"change\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"Set payload\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload.vehicles\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1390,\"y\":900,\"wires\":[[\"726a8da3fe930e54\"]]},{\"id\":\"bc891dc2aaaedc39\",\"type\":\"change\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"Change and delete properties\",\"rules\":[{\"t\":\"set\",\"p\":\"payload.lat\",\"pt\":\"msg\",\"to\":\"payload.latitude\",\"tot\":\"msg\"},{\"t\":\"delete\",\"p\":\"payload.latitude\",\"pt\":\"msg\"},{\"t\":\"set\",\"p\":\"payload.lon\",\"pt\":\"msg\",\"to\":\"payload.longitude\",\"tot\":\"msg\"},{\"t\":\"delete\",\"p\":\"payload.longitude\",\"pt\":\"msg\"},{\"t\":\"set\",\"p\":\"payload.color\",\"pt\":\"msg\",\"to\":\"blue\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"payload.name\",\"pt\":\"msg\",\"to\":\"payload.vehicle_id\",\"tot\":\"msg\"},{\"t\":\"delete\",\"p\":\"payload.vehicle_id\",\"pt\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":1770,\"y\":900,\"wires\":[[\"b3dd7814ea5270ca\"]]},{\"id\":\"726a8da3fe930e54\",\"type\":\"split\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"\",\"splt\":\"\\\\n\",\"spltType\":\"str\",\"arraySplt\":1,\"arraySpltType\":\"len\",\"stream\":false,\"addname\":\"\",\"x\":1550,\"y\":900,\"wires\":[[\"bc891dc2aaaedc39\"]]},{\"id\":\"b3dd7814ea5270ca\",\"type\":\"switch\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"is vehical type is tram\",\"property\":\"payload.vehicle_type\",\"propertyType\":\"msg\",\"rules\":[{\"t\":\"eq\",\"v\":\"tram\",\"vt\":\"str\"},{\"t\":\"else\"}],\"checkall\":\"true\",\"repair\":false,\"outputs\":2,\"x\":2040,\"y\":900,\"wires\":[[\"0c5d6bcfd71d40da\"],[\"e037ee3d1f702d25\"]]},{\"id\":\"0c5d6bcfd71d40da\",\"type\":\"change\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"set icon and icon color for tram\",\"rules\":[{\"t\":\"set\",\"p\":\"payload.icon\",\"pt\":\"msg\",\"to\":\"fa-train\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"payload.iconColor\",\"pt\":\"msg\",\"to\":\"yellow\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":2310,\"y\":860,\"wires\":[[\"b4f2e2dabd5b8220\"]]},{\"id\":\"e037ee3d1f702d25\",\"type\":\"change\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"set icon and icon color for bus\",\"rules\":[{\"t\":\"set\",\"p\":\"payload.icon\",\"pt\":\"msg\",\"to\":\"bus\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"payload.iconColor\",\"pt\":\"msg\",\"to\":\"red\",\"tot\":\"str\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":2310,\"y\":940,\"wires\":[[\"b4f2e2dabd5b8220\"]]},{\"id\":\"d7ca6c3cdf176e4e\",\"type\":\"ui-iframe\",\"z\":\"eacc68e72f120b0e\",\"g\":\"4e45e8ef870b86fb\",\"name\":\"\",\"group\":\"15d2dfa55e99ea43\",\"order\":0,\"src\":\"/worldmap\",\"width\":\"12\",\"height\":\"10\",\"x\":1370,\"y\":1040,\"wires\":[]},{\"id\":\"15d2dfa55e99ea43\",\"type\":\"ui-group\",\"name\":\"U.K Transportation Live\",\"page\":\"e098e3047b4a4eaa\",\"width\":\"12\",\"height\":\"1\",\"order\":-1,\"showTitle\":false,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"e098e3047b4a4eaa\",\"type\":\"ui-page\",\"name\":\"U.K Transportation Live\",\"ui\":\"c2e1aa56f50f03bd\",\"path\":\"/worldmap\",\"icon\":\"earth\",\"layout\":\"grid\",\"theme\":\"129e99574def90a3\",\"order\":-1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"c2e1aa56f50f03bd\",\"type\":\"ui-base\",\"name\":\"Dashboard\",\"path\":\"/dashboard\",\"showPathInSidebar\":false,\"navigationStyle\":\"default\"},{\"id\":\"129e99574def90a3\",\"type\":\"ui-theme\",\"name\":\"Another Theme\",\"colors\":{\"surface\":\"#000000\",\"primary\":\"#ff4000\",\"bgPage\":\"#f0f0f0\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#d9d9d9\"},\"sizes\":{\"pagePadding\":\"9px\",\"groupGap\":\"12px\",\"groupBorderRadius\":\"9px\",\"widgetGap\":\"6px\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow136.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-136') })</script>
<ol>
<li>With your flow updated to include the above, click the <strong>Deploy</strong> button in the top-right of the Node-RED Editor.</li>
<li>Locate the <strong>Open Dashboard</strong> button at the top-right corner of the Dashboard 2.0 sidebar and click on it to navigate to the dashboard.</li>
</ol>
<p>Now you can view the live location of Edinburgh public transport vehicles on the dashboard. Additionally, clicking on each vehicle reveals further details such as its name, speed, and other properties you've included. Moreover, if you wish to track the live locations of your own vehicles instead of Edinburgh's public transport vehicles, you can connect your devices and access GPS and sensor data using the <a href="https://flowfuse.com/product/device-agent/">Flowfuse device agent</a>.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/mapping-location-on-dashboard-2/#conclusion"></a> Conclusion</h2>
<p>In conclusion, this guide shows an easy way to map location data on Dashboard 2.0. By following these steps, you can make interactive dashboards that give you real-time info, useful for things like managing fleets and tracking logistics.</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Mapping%20location%20data%20within%20Node-RED%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/Comprehensive guide: Node-RED Dashboard 2.0 layout, sidebar, and stylingExplore Dashboard 2.0 Different layouts and sidebars. learn how to style Dashboard 2.0 elements effortlessly.2024-05-10T00:00:00ZSumit Shinde<p>In this comprehensive guide, we will explore different layouts and sidebar styles in Dashboard 2.0. Additionally, we will cover how you can style Dashboard 2.0 elements effortlessly.</p>
<!--more-->
<p>If you are new to Dashboard 2.0, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting started with Node-RED Dashboard 2.0</a> and make sure you have installed it.</p>
<h2 id="understanding-dashboard-2.0-layouts." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#understanding-dashboard-2.0-layouts."></a> Understanding Dashboard 2.0 layouts.</h2>
<p>A layout in Node-RED Dashboard 2.0 refers to how groups of widgets are organized and arranged on a page. It controls the visual structure and placement of these widget groups to create an organized and easy-to-use interface.</p>
<h3 id="exploring-dashboard-2.0-layouts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#exploring-dashboard-2.0-layouts"></a> Exploring Dashboard 2.0 layouts</h3>
<p>In Dashboard 2.0, we have three types of layouts: Grid, Notebook, and Fixed.</p>
<h4 id="grid-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#grid-layout"></a> Grid layout</h4>
<p>Choosing this layout divides your dashboard page into <strong>12 equally-sized columns</strong>, and you can specify how many columns your group will occupy using the <code>size</code> property. When groups within a row take up all available columns, a new row automatically starts. The height of each row is determined by the tallest widget in that row.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-grid-layout-PvLESQMGtM-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-grid-layout-PvLESQMGtM-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of dashboard having grid layout" alt=""Screenshot of dashboard having grid layout"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-grid-layout-PvLESQMGtM-1300.jpeg" width="1300" height="612" /></picture></p>
<p>In the image above, you can see that the first and last widget groups occupy all 12 columns, while in the middle, two groups each take up six columns.</p>
<h4 id="notebook-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#notebook-layout"></a> Notebook layout</h4>
<p>Choosing the Notebook layout for your page in Dashboard 2.0 makes it work like a Jupyter Notebook, fixed at a width of <strong>1024px</strong> and <strong>centered</strong>. Here, a groups' "width" defines the number of columns the group contains. The group itself will always render the full width of the Notebook. It's great for dynamic Markdown, data tables, and visuals. Groups of pages are stacked vertically.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-notebook-layout-9bMA-4n-cN-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-notebook-layout-9bMA-4n-cN-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of dashboard having notebook layout" alt=""Screenshot of dashboard having notebook layout"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-notebook-layout-9bMA-4n-cN-1920.jpeg" width="1920" height="1104" /></picture></p>
<h4 id="fixed-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#fixed-layout"></a> Fixed layout</h4>
<p>In this layout, the width value is converted to "units", with each unit being <code>90px</code> wide. For example, if you set the group width to <code>3</code>, it will be 3 * 90 = 270px wide. Within a given group, the group size represents a column in the group's internal grid, following the same pattern as other layouts.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fix-layout--MZdJybdJB-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fix-layout--MZdJybdJB-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of dashboard having fixed layout" alt=""Screenshot of dashboard having fixed layout"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fix-layout--MZdJybdJB-1920.jpeg" width="1920" height="1114" /></picture></p>
<p><em>Note: Currently this layout is not optimised, with plans to make it similar to Dashboard 1.0 in how it compresses content vertically, so it is recommended to use other layouts.</em></p>
<h3 id="setting-page-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#setting-page-layout"></a> Setting page layout</h3>
<ol>
<li>Navigate to the page configuration by clicking on the <strong>edit</strong> button of your page in the Dashboard 2.0 sidebar.</li>
<li>In the page configuration, you can select the preferred layout for that page within the layout field.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-new-page-layout-t4-nc8b_LN-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing process of setting page layout" alt=""Image showing process of setting page layout"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-new-page-layout-t4-nc8b_LN-800.webp" width="800" height="450" /></picture></p>
<h2 id="setting-dashboard-2.0-elements-size" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#setting-dashboard-2.0-elements-size"></a> Setting Dashboard 2.0 elements size</h2>
<p>Setting the size for elements in Dashboard 2.0 is straightforward, but understanding the actual unit size in the size property can be a bit tricky.</p>
<p>It's important to note that the size of a single horizontal unit varies depending on the layout, but the vertical size of a single row is consistently <strong>48px</strong>.</p>
<h2 id="sizing-widgets-within-a-group" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#sizing-widgets-within-a-group"></a> Sizing Widgets within a Group</h2>
<p>In any layout—Grid, Notebook, or Fixed—widgets within a group are sized using a unified approach. The size property assigned to widgets determines their width within the group. Each unit in the size property represents a fraction of the group's total width. This width is determined by an internal grid established by the group.</p>
<h3 id="widget-sizing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#widget-sizing"></a> Widget Sizing</h3>
<p>Widgets are sized relative to the number of columns in the internal grid. For example, if a group has 4 columns and two widgets, and the first widget is set to 1 width while the second to 3 width, the first widget will occupy 25% of the group's width, and the second widget will occupy 75%.</p>
<p>Regardless of the layout type, the concept of sizing widgets within a group remains consistent. Whether it's the grid, notebook, or fixed layout, the same principles apply, ensuring uniformity in widget layout and design.</p>
<h3 id="setting-element-size" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#setting-element-size"></a> Setting element size</h3>
<p>To set the size of groups and widgets in Dashboard 2.0, follow these steps:</p>
<ol>
<li>Go to the Dashboard 2.0 sidebar and click on the edit button next to the element you want to resize.</li>
<li>Adjust the size using the size property.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-size-NIkb2CpERL-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing process of setting element size" alt=""Image showing process of setting element size"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-size-NIkb2CpERL-800.webp" width="800" height="450" /></picture></p>
<h2 id="understanding-dashboard-2.0-theme" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#understanding-dashboard-2.0-theme"></a> Understanding Dashboard 2.0 Theme</h2>
<p>The theme is a collection of colors that control the look and feel of the widgets, groups, and other elements on the page.</p>
<p>In Dashboard 2.0, when adding a page ( ui-page ) we can specify which theme it will use. By default, we have one theme in Dashboard 2.0, we can add more themes using the Dashboard 2.0 side panel.</p>
<h3 id="understanding-theme-properties" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#understanding-theme-properties"></a> Understanding theme properties</h3>
<p>In the theme (<code>ui-theme</code>) configuration, there are two main sections:</p>
<ul>
<li><strong>Colors:</strong> Specify colors for Navigation, primary elements, page background, group backgrounds, and outlines.</li>
<li><strong>Sizing:</strong> Define the gaps between groups, page padding, group outline radius, and gaps between widgets, all in pixels.</li>
</ul>
<p>For additional information on the <code>ui-theme</code> settings, please refer to the <a href="https://dashboard.flowfuse.com/nodes/config/ui-theme.html">ui-theme documentation</a>.</p>
<h3 id="setting-a-new-page-theme" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#setting-a-new-page-theme"></a> Setting a new page theme</h3>
<ol>
<li>Navigate to the Dashboard 2.0 sidebar and switch to the theme tab.</li>
<li>Click on the top-right “+theme” button to add a new theme.</li>
<li>After specifying colors and sizing click on the top right update button to save the theme.</li>
<li>Now switch to the layout tab and click on the edit button next to the page for which you want to set a new theme.</li>
<li>In the page config, select the newly added theme in the Theme field.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-adding-new-theme-SLMXZ2ekX1-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing process of adding new theme" alt=""Image showing process of adding new theme"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-adding-new-theme-SLMXZ2ekX1-800.webp" width="800" height="450" /></picture></p>
<h2 id="dashboard-2.0-navigation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#dashboard-2.0-navigation"></a> Dashboard 2.0 Navigation</h2>
<h3 id="setting-sidebar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#setting-sidebar"></a> Setting sidebar</h3>
<ol>
<li>Navigate to the Dashboard 2.0 sidebar in the Node-RED editor</li>
<li>Click on the "Edit Settings" button located at the top left side of the Dashboard 2.0 sidebar.</li>
<li>Select your preferred sidebar style from the "Style" field in the sidebar options section.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-sidebar-lM5FuCMcHa-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing process of changing sidebar style" alt=""Image showing process of changing sidebar style"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-setting-sidebar-lM5FuCMcHa-800.webp" width="800" height="450" /></picture></p>
<h3 id="sidebar-navigation-options" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#sidebar-navigation-options"></a> Sidebar Navigation Options</h3>
<p>In Dashboard 2.0, we have 5 different navigation options for your application.</p>
<h4 id="collapsing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#collapsing"></a> Collapsing</h4>
<p>This is the default sidebar, when it's opened, the page content adjusts to the width of the sidebar.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-collapsing-sidebar-Tl5KobJ_vt-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing Collapsing sidebar" alt=""Image showing 'Collapsing' sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-collapsing-sidebar-Tl5KobJ_vt-800.webp" width="800" height="450" /></picture></p>
<p>You can see in the image above how the page content automatically adjusts when the sidebar is opened.</p>
<h4 id="fixed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#fixed"></a> Fixed</h4>
<p>In this type, the sidebar is always visible and fixed on the left side, and the top menu icon is hidden. The page content adjusts to the width of the sidebar.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fixed-layout-oHn4J0SP_P-1817.avif 1817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fixed-layout-oHn4J0SP_P-1817.webp 1817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing Fixed sidebar" alt=""Image showing 'Fixed' sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-fixed-layout-oHn4J0SP_P-1817.jpeg" width="1817" height="881" /></picture></p>
<h4 id="collapse-to-icon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#collapse-to-icon"></a> Collapse to icon</h4>
<p>This type of sidebar is similar to the collapsible one, but when the sidebar is collapsed, you can still navigate through different pages as the page icons become visible.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-collaps-to-icon-sidebar-PlEf-y77wk-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image showing Collapse to icon sidebar" alt=""Image showing 'Collapse to icon' sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-collaps-to-icon-sidebar-PlEf-y77wk-800.webp" width="800" height="450" /></picture></p>
<h4 id="apear-over-content" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#apear-over-content"></a> Apear over content</h4>
<p>When the sidebar is opened, the page is partially covered by a transparent layer, and the sidebar appears on top of this layer</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-appear-over-content-MrNk8Z8v83-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="screenshot displaying searching for botFather bot for creating custom bot" alt=""Image showing 'Apear over content' sidebar"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-appear-over-content-MrNk8Z8v83-800.webp" width="800" height="450" /></picture></p>
<p>In this type of sidebar, you can notice how the sidebar opens without affecting the width of the page content</p>
<h4 id="always-hide" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#always-hide"></a> Always hide</h4>
<p>In this type, the sidebar is always hidden, and navigation between different pages can be achieved using the ui-control widget.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-always-hidden-gKRjmUT8t0-1815.avif 1815w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-always-hidden-gKRjmUT8t0-1815.webp 1815w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="screenshot displaying searching for botFather bot for creating custom bot" alt=""screenshot displaying searching for botFather bot for creating custom bot"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-always-hidden-gKRjmUT8t0-1815.jpeg" width="1815" height="893" /></picture></p>
<h2 id="customising-your-dashboard-2.0-further" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#customising-your-dashboard-2.0-further"></a> Customising your Dashboard 2.0 further</h2>
<p>In Dashboard 2.0, we can add classes to almost all widgets, pages, and groups and style them using CSS.</p>
<h3 id="adding-classes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#adding-classes"></a> Adding classes</h3>
<ol>
<li>To add classes to your widget, page, or group, you'll need to open its configuration</li>
<li>Find the 'Class' field and enter your class.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-showing-class-property-feild-2nlAvyNGnt-510.avif 510w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-showing-class-property-feild-2nlAvyNGnt-510.webp 510w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing the class property input field" alt=""Screenshot showing the class property input field"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-showing-class-property-feild-2nlAvyNGnt-510.jpeg" width="510" height="702" /></picture></p>
<h3 id="writing-custom-css" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#writing-custom-css"></a> Writing custom CSS</h3>
<p>In Dashboard 2.0, the <code>ui-template</code> node allows you to write custom CSS for Dashboard 2.0.</p>
<p>In the template node, you can add CSS for two different scopes:</p>
<ul>
<li><strong>Single Page:</strong> Selecting this allows you to specify CSS that is constrained to a single page of your dashboard.</li>
<li><strong>All Pages:</strong> Selecting this allows you to define CSS that will apply across your whole dashboard.</li>
</ul>
<p>To define your own CSS:</p>
<ol>
<li>Drag an ui-template widget onto the canvas.</li>
<li>Double-click on it and select the scope within the type field.</li>
<li>If you select the "CSS (Single Page)" type, you'll then need to select the <code>ui-page</code> to which your custom class definitions will apply. If you select the "CSS (All Pages)" type, then you'll need to select the <code>ui-base</code> that includes those pages to which you want to add styling.</li>
<li>Now you can write your custom CSS within the ui-template.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-adding-style-MpRk_PX8tk-1850.avif 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-adding-style-MpRk_PX8tk-1850.webp 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Image displaying the left side with the page and group where custom CSS has been applied, and the right side showcasing the UI-template with the corresponding CSS" alt=""Image displaying the left side with the page and group where custom CSS has been applied, and the right side showcasing the UI-template with the corresponding CSS"" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-dashboard-2-layout-navigation-styling-adding-style-MpRk_PX8tk-1850.jpeg" width="1850" height="842" /></picture></p>
<h2 id="up-next" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-dashboard-2-layout-navigation-styling/#up-next"></a> Up Next</h2>
<p>To delve deeper into Node-RED Dashboard 2.0, we recommend exploring the following resources:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/dashboard/">FlowFuse Dashboard Articles</a> - Collection of examples and guides written by FlowFuse.</li>
<li><a href="https://dashboard.flowfuse.com/">Node-RED Dashboard 2.0 Documentation</a> - Detailed information for each of the nodes available in Dashboard 2.0, as well as useful guides on building custom nodes and widgets of your own.</li>
<li><a href="https://discourse.nodered.org/tag/dashboard-2">Node-RED Forums - Dashboard 2.0</a> - The Node-RED forums are a great place to ask questions, share your projects and get help from the community.</li>
<li><a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">Beginner Guide to a Professional Node-RED</a> - A free guide to an enterprise-ready Node-RED. Learn all about Node-RED history, securing your flows, and dashboard data visualization.</li>
</ul>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Comprehensive%20guide%3A%20Node-RED%20Dashboard%202.0%20layout%2C%20sidebar%2C%20and%20styling">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/FlowFuse 2.4: making it easier to work with Snapshots, Blueprints & DevicesOur latest release introduces better ways to work with Snapshots, Blueprints, view the content of you flows in FlowFuse, and manage the version of Node-RED running on Devices2024-05-09T00:00:00ZRob Marcer<p>FlowFuse 2.4 introduces better ways to work with Snapshots, Blueprints, view the content of you flows in FlowFuse, and manage the version of Node-RED running on Devices</p>
<!--more-->
<h2 id="export-snapshots-from-flowfuse-%233627" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#export-snapshots-from-flowfuse-%233627"></a> Export Snapshots from FlowFuse <a href="https://github.com/FlowFuse/flowfuse/issues/3627">#3627</a></h2>
<p>You can now export your Snapshots of your Node-RED instances. This is our first step towards making it easier to move Snapshots in and out or your FlowFuse projects, as well as share them with other teams. We will be adding many more Snapshot management features over the next few releases.</p>
<h2 id="easier-creation-of-node-red-instances-from-blueprints-%233729" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#easier-creation-of-node-red-instances-from-blueprints-%233729"></a> Easier creation of Node-RED instances from Blueprints <a href="https://github.com/FlowFuse/flowfuse/issues/3729">#3729</a></h2>
<p>We introduced our library of Node-RED <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/">Blueprints</a> in October 2023. FlowFuse Blueprints aim to make the Node-RED experience more accessible for newcomers, while also offering a treasure trove of fresh ideas for seasoned Node-RED users. In this release, we are making it even easier to start a new Node-RED instance based on a Blueprint from our library.</p>
<h2 id="view-flows-within-the-team-library-without-loading-node-red-%233803" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#view-flows-within-the-team-library-without-loading-node-red-%233803"></a> View Flows within the Team Library without loading Node-RED <a href="https://github.com/FlowFuse/flowfuse/issues/3803">#3803</a></h2>
<p>In September 2023, Node-RED community member <a href="https://github.com/gorenje">gorenje</a> created some code that could be used to visualise Node-RED flows without having to load the full Node-RED editor. This was added to the community <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/">flow library</a> and has proven to be a great addition.</p>
<p>With gorenje's support, we've packaged up his library to make it easier to embed in other sites and have made it generally available <a href="https://github.com/FlowFuse/flow-renderer">here</a>. A big thanks to gorenje for laying the ground work and allow us to build on it.</p>
<p>We've wasted no time putting this to good use within FlowFuse. Our first step is in our <a href="https://flowfuse.com/changelog/2024/05/library-flowviewer/">Team Library</a>. You can now view a visual representation of saved flows in your Team Library.</p>
<h2 id="view-flows-within-snapshots-without-loading-node-red-%233798" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#view-flows-within-snapshots-without-loading-node-red-%233798"></a> View Flows within Snapshots without loading Node-RED <a href="https://github.com/FlowFuse/flowfuse/issues/3798">#3798</a></h2>
<p>The second place we are adding the ability to preview flows is to the Snapshots interface. Again, based on <a href="https://github.com/gorenje/node-red-flowviewer-js">node-red-flowviewer-js</a>, the Flow Viewer will make loading the snapshot you need into your Node-RED instance quicker and easier.</p>
<h2 id="manage-node-red-versions-on-devices-%233766" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#manage-node-red-versions-on-devices-%233766"></a> Manage Node-RED versions on Devices <a href="https://github.com/FlowFuse/flowfuse/pull/3766">#3766</a></h2>
<p>It can be useful to select a specific version of Node-RED to run, for example where you are using a custom node which only runs on specific versions of Node-RED. In this release we have added the ability to easily select the version of Node-RED you wish to run on your devices.</p>
<h2 id="full-list-of-release-features-and-bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#full-list-of-release-features-and-bug-fixes"></a> Full list of release features and bug fixes</h2>
<p>You can view everything included in 2.4 on the <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v2.4.0">Github Release page</a>.</p>
<p>We also regularly release updates to <a href="https://app.flowfuse.com/">FlowFuse Cloud</a> in between our monthly releases. You can follow the updates as they are made via our <a href="https://flowfuse.com/changelog/">ChangeLog</a>.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 2.4.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/flowfuse-2-4-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/Understanding Node, Flow, Global, and Environment Variables in Node-REDGuide to Understanding and Managing Node-RED Variables for Efficient Workflows2024-05-06T00:00:00ZSumit Shinde<p>Node-RED is a low-code programming tool for connecting hardware, APIs, and online services. While its visual interface makes development accessible, understanding how to work with variables like node, flow, global, and environment is key to building efficient, organized flows.</p>
<!--more-->
<p>This guide provides a clear explanation of each variable type in Node-RED, including how to initialize, access, and manage them effectively. Whether you are just starting with Node-RED or looking to improve your workflows, this article will help you gain a solid understanding of the variable context system and how to use it to your advantage.</p>
<h2 id="understanding-node-red-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#understanding-node-red-variables"></a> Understanding Node-RED Variables</h2>
<p>Variables help you store and manage data in Node-RED. There are three main types: message, context, and environment variables.</p>
<p>Message variables are part of the message as it moves through the flow. For example, <code>msg.payload</code> carries the main data between nodes. You can learn more about messages in the <a href="https://flowfuse.com/node-red/getting-started/node-red-messages/">Understanding Node-RED Messages</a> guide.</p>
<p>Context variables let you store information about the current state of the application. These can be saved at the node level, the flow level, or globally across the whole instance.</p>
<p>Environment variables are used for configuration and often store sensitive data like secrets or API keys. They help keep important information safe and separate from the flow itself.</p>
<h3 id="exploring-global-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-global-variables"></a> Exploring Global variables</h3>
<p>Global variables in Node-RED are accessible to function, change, inject, and switch nodes within a given Node-RED instance. They serve as a centralized storage point for data that needs to be accessed across different parts of an application. This is especially useful when you want to share data across multiple flows or tabs within the same Node-RED instance.</p>
<p>For example, in a home automation system with flows for lighting, security, and climate control, global variables can store user preferences or system settings that all flows can access and update. This allows for consistent behavior across the entire system.</p>
<h4 id="initiating%2Fsetting-global-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#initiating%2Fsetting-global-variable"></a> Initiating/setting Global variable</h4>
<p>We can set Global variables using the function and change node.</p>
<p>In the change node, you can set it as shown in the below image</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-setting-global-variable-using-change-node-PsJ_ZjnUK7-783.avif 783w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-setting-global-variable-using-change-node-PsJ_ZjnUK7-783.webp 783w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to set global variable using the change node" alt=""Screenshot showing how to set global variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-setting-global-variable-using-change-node-PsJ_ZjnUK7-783.jpeg" width="783" height="545" /></picture></p>
<p>To initiate a global variable using the function node, use the global's <code>set</code> method as shown below:</p>
<div style="position: relative" id="code-container-46">
<pre class="language-javascript"><code id="code-46" class="language-javascript">global<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-46" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="retrieving-global-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#retrieving-global-variable"></a> Retrieving Global variable</h4>
<p>Retrieving global variables using change, inject, and switch nodes is quite similar. You simply need to select the "global" option and enter the variable name in the input field. Below is an image showing how you can retrieve global variables using the change node:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-global-variable-using-change-node-zPrLhlGijO-793.avif 793w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-global-variable-using-change-node-zPrLhlGijO-793.webp 793w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to retrieve global variables using the change node" alt=""Screenshot showing how to retrieve global variables using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-retrieving-global-variable-using-change-node-zPrLhlGijO-793.jpeg" width="793" height="565" /></picture></p>
<p>To retrieve a global variable using the function, use the global's <code>get</code> method like the below in the function node:</p>
<div style="position: relative" id="code-container-59">
<pre class="language-javascript"><code id="code-59" class="language-javascript">global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-59" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="deleting-global-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#deleting-global-variables"></a> Deleting Global variables</h4>
<p>To delete the global variables again you can use both the "context data" tab and the change node. I have shown in the below image how you can delete global variables using the change node:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-deleting-global-variable-using-change-node-rHUKmU_0ie-769.avif 769w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-deleting-global-variable-using-change-node-rHUKmU_0ie-769.webp 769w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to delete global variable using the change node" alt=""Screenshot showing how to delete global variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-deleting-global-variable-using-change-node-rHUKmU_0ie-769.jpeg" width="769" height="561" /></picture></p>
<h3 id="exploring-flow-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-flow-variables"></a> Exploring Flow variables</h3>
<p>Flow variables are accessible to function, change, inject, and switch nodes and some third-party nodes within the same tab or flow where they have been set. It is useful for sharing data within a specific flow or tab, allowing for seamless data transfer between nodes within the same flow.</p>
<p>For example, in a temperature monitoring system, you have multiple sensors sending data to different nodes within the same flow. You can use flow variables to pass the current temperature reading between nodes for processing and analysis within that specific flow.</p>
<h4 id="initiating%2Fsetting-flow-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#initiating%2Fsetting-flow-variable"></a> Initiating/Setting Flow variable</h4>
<p>We can set flow variables using function and change node.</p>
<p>In the change node, you can set it as shown in the below image</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-setting-flow-variable-using-change-node-0Dmq_-33iw-792.avif 792w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-setting-flow-variable-using-change-node-0Dmq_-33iw-792.webp 792w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to set flow variable using the change node" alt=""Screenshot showing how to set flow variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-setting-flow-variable-using-change-node-0Dmq_-33iw-792.jpeg" width="792" height="577" /></picture></p>
<p>To initiate a flow variable using the function node, use the flow's <code>set</code> method as shown below:</p>
<div style="position: relative" id="code-container-93">
<pre class="language-javascript"><code id="code-93" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-93" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="retrieving-flow-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#retrieving-flow-variable"></a> Retrieving Flow variable</h4>
<p>You can retrieve flow variables using the function, change, inject, and switch.</p>
<p>Retrieving flow variables using change, inject, and switch nodes is quite similar. You simply need to select the "flow" option and enter the variable name in the input field. Below is an image showing how you can retrieve flow variables using the change node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-flow-variable-using-change-node-_Id1_SeBgg-781.avif 781w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-flow-variable-using-change-node-_Id1_SeBgg-781.webp 781w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to retrieve flow variable using the change node" alt=""Screenshot showing how to retrieve flow variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-retrieving-flow-variable-using-change-node-_Id1_SeBgg-781.jpeg" width="781" height="579" /></picture></p>
<p>To retrieve a flow variable, use the flow's <code>get</code> method like the below in the function node:</p>
<div style="position: relative" id="code-container-109">
<pre class="language-javascript"><code id="code-109" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-109" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="deleting-flow-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#deleting-flow-variables"></a> Deleting Flow variables</h4>
<p>To delete flow variables you can use both the "context data" tab and the change node. I have shown in the below image how you can delete flow variables using the change node:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-deleting-flow-variable-using-change-node-mmmwn-FZzk-767.avif 767w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-deleting-flow-variable-using-change-node-mmmwn-FZzk-767.webp 767w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to delete flow variable using the change node" alt=""Screenshot showing how to delete flow variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-deleting-flow-variable-using-change-node-mmmwn-FZzk-767.jpeg" width="767" height="557" /></picture></p>
<h3 id="exploring-node-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-node-variables"></a> Exploring Node variables</h3>
<p>Node variables are specific to each node and are only visible within that node. This means we cannot read or write into that variable from outside of the node where it is initialized. This is ideal for cases where you want to store data specific to a single node, to ensure data isolation and prevent interference with other nodes</p>
<p>For example, You have a form on a Node-RED Dashboard 2.0, and you want to insert the submitted data into a database along with a unique ID for each submission. You can use a node variable to store and track a counter variable in the function node.</p>
<h4 id="initiating%2Fsetting-node-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#initiating%2Fsetting-node-variable"></a> Initiating/setting Node variable</h4>
<p>To initiate and set a node variable using the function node, you will have to use the context’s <code>set</code> method:</p>
<div style="position: relative" id="code-container-134">
<pre class="language-javascript"><code id="code-134" class="language-javascript">context<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-134" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This action sets the value for the context variable with the specified name.</p>
<h4 id="retrieving-node-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#retrieving-node-variable"></a> Retrieving Node variable</h4>
<p>To retrieve a node variable using a function node, use the context's <code>get</code> method:</p>
<div style="position: relative" id="code-container-144">
<pre class="language-javascript"><code id="code-144" class="language-javascript">context<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-144" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="deleting-node-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#deleting-node-variables"></a> Deleting Node variables</h4>
<p>Node variables cannot be deleted using a Node-RED node. To delete them, use the Context Data tab in the Node-RED editor, where a delete option is available for each variable. For more details, refer to the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-context-data-tab">Exploring the Context Data tab</a> section of this guide.</p>
<h3 id="flowfuse-persistent-storage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#flowfuse-persistent-storage"></a> FlowFuse persistent storage</h3>
<p>So far, we have seen three types of context variables and all are stored in memory. This means that when we stop the Node-RED instance, the variables will get wiped out.</p>
<p>FlowFuse provides a way to persist these variable values between restarts and FlowFuse stack updates. For more details, refer to <a href="https://flowfuse.com/docs/user/persistent-context/">FlowFuse Persistent Context</a>.</p>
<h4 id="setting-persistent-variables-in-function-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#setting-persistent-variables-in-function-node"></a> Setting Persistent Variables in Function Node</h4>
<p>To set variables in persistent store using change node steps are the same as storing them in memory you just need to change the store option to persistent from memory in the change node like below:</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/variables-in-node-red-change-node-persistent-store-option-for-while-setting-variable-QTRapeb_qD-538.gif 538w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to set global variable using the change node" alt=""Screenshot showing how to set global variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-change-node-persistent-store-option-for-while-setting-variable-QTRapeb_qD-538.webp" width="538" height="366" /></picture></p>
<p>In the function node, you just need to pass one more argument "persistent" in the set method.</p>
<div style="position: relative" id="code-container-172">
<pre class="language-javascript"><code id="code-172" class="language-javascript">global<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span>value<span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-172" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-173">
<pre class="language-javascript"><code id="code-173" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span>value<span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-173" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-174">
<pre class="language-javascript"><code id="code-174" class="language-javascript">context<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span>value<span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-174" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="retrieving-persistent-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#retrieving-persistent-variables"></a> Retrieving Persistent Variables</h4>
<p>You can retrieve variables stored in the persistent store using the change node as shown in the below image:</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/variables-in-node-red-change-node-persistent-store-option--UYd_d_m4p-528.gif 528w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to retrieve global variable using the change node" alt=""Screenshot showing how to retrieve global variable using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-change-node-persistent-store-option--UYd_d_m4p-528.webp" width="528" height="366" /></picture></p>
<p>To retrieve a variable stored in the persistent store using the function node, you'll need to pass "persistent" in the get method.</p>
<div style="position: relative" id="code-container-187">
<pre class="language-javascript"><code id="code-187" class="language-javascript">global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-187" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-188">
<pre class="language-javascript"><code id="code-188" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-188" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-189">
<pre class="language-javascript"><code id="code-189" class="language-javascript">context<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">,</span><span class="token string">'persistent'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-189" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="exploring-context-data-tab" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-context-data-tab"></a> Exploring Context Data tab</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-context-data-tab-Xe8QU6UIZF-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-context-data-tab-Xe8QU6UIZF-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing Node-RED Context data tab" alt=""Screenshot showing Node-RED Context data tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-context-data-tab-Xe8QU6UIZF-1851.jpeg" width="1851" height="904" /></picture></p>
<p>Node-RED provides a dedicated interface for viewing and managing all Node-RED variables. Navigate to the sidebar's "Context Data" tab, where you'll find sections for Node, Flow, and Global variables. Each section has a refresh icon at the top right corner; click on it to see the latest or newly added variables.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-context-data-tab-options-for-varrables-6zlsRgfchy-485.avif 485w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-context-data-tab-options-for-varrables-6zlsRgfchy-485.webp 485w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing Node-RED Context data tab options for managing variables" alt=""Screenshot showing Node-RED Context data tab options for managing variables"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-context-data-tab-options-for-varrables-6zlsRgfchy-485.jpeg" width="485" height="117" /></picture></p>
<p>In this tab, you'll also find information about when each variable was set or updated, along with additional options on the right side of each variable. The first option allows you to copy the variable's name, the second option lets you copy the variable's value, the third option refreshes the variable to show the most recent value, and the fourth option allows you to delete the variable.</p>
<h3 id="exploring-environmental-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#exploring-environmental-variables"></a> Exploring Environmental variables</h3>
<p>Environment variables are specifically used for storing sensitive configuration data, such as API keys or database credentials, ensuring this information isn't directly exposed within your flows. In Node-RED you can set environment variables at flow and global level.</p>
<ul>
<li>
<p>Flow-level environment: Used to store sensitive configuration data accessible only within a specific flow. This ensures secure and isolated storage of sensitive information. For example, when building a multi-flow Node-RED application, each flow may need different configuration details, like API keys or unique identifiers. Using flow-level environment variables allows each flow to securely store its specific sensitive data without exposing it to other flows.</p>
</li>
<li>
<p>Global-level environment: Variables are used to store sensitive data accessible across all flows in a Node-RED instance. They are helpful when you need to share the same sensitive data across different flows. For example, If multiple flows need to use the same API key, setting a global-level environment variable allows them to access this data securely, avoiding repeated configurations and ensuring consistency.</p>
</li>
</ul>
<h4 id="setting-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#setting-environment-variables"></a> Setting Environment Variables</h4>
<p>To set flow-level environment variables you'll have to use the edit dialog of the flow.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/variables-in-node-red-setting-flow-scope-enviroment-variable-ewYXJbjVyF-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to set flow level environment variables" alt=""Screenshot showing how to set flow level environment variables"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-setting-flow-scope-enviroment-variable-ewYXJbjVyF-800.webp" width="800" height="450" /></picture></p>
<p>For information on setting and managing global-level environment variables, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment variables in Node-RED</a>.</p>
<h4 id="accessing-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#accessing-environment-variables"></a> Accessing Environment variables</h4>
<p>Environment variables in the change, inject, and switch nodes can be accessed by selecting the "$ env variable" option and entering the variable name in the input field. Here's an example of Accessing an environment variable using the change node:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-environment-variable-using-change-node-yYpTXprVol-771.avif 771w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/variables-in-node-red-retrieving-environment-variable-using-change-node-yYpTXprVol-771.webp 771w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot showing how to retrieve environment variable in the change node" alt=""Screenshot showing how to retrieve environment variable in the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/variables-in-node-red-retrieving-environment-variable-using-change-node-yYpTXprVol-771.jpeg" width="771" height="539" /></picture></p>
<p>To access environment variables in a function node, use:</p>
<div style="position: relative" id="code-container-247">
<pre class="language-javascript"><code id="code-247" class="language-javascript">env<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'variableName'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-247" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>In the template node, you can access it as shown below:</p>
<div style="position: relative" id="code-container-251">
<pre class="language-javascript"><code id="code-251" class="language-javascript">This is my username <span class="token operator">:</span> <span class="token punctuation">{</span><span class="token punctuation">{</span>env<span class="token punctuation">.</span><span class="token constant">USERNAME</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">.</span><br /></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-251" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>And if you need to access environment variables in third-party configuration nodes where they haven't provided an option to use environment variables, you can access them with <code>${variableName}</code> in the node's input field.</p>
<p><em>Note: If a flow-level and a global-level environment variable share the same name, Node-RED prioritizes the flow-level variable. To access the global-level variable in this case, prefix the variable name with <code>$parent</code>.</em></p>
<h4 id="deleting-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#deleting-environment-variables"></a> Deleting Environment variables</h4>
<p>To delete added environment variables, you can use the same interface where added them. In the right corner of your added environment variable, you'll see a delete or cross icon. Simply click on it to delete the variable and restart your Node-RED instance.</p>
<p>By now, you should have a complete understanding of how variables function within Node-RED, how to set, retrieve, and delete them, both in memory and in persistent storage.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/understanding-node-flow-global-environment-variables-in-node-red/#conclusion"></a> Conclusion</h2>
<p>Building with Node-RED becomes much easier when you understand how data moves and is stored throughout your flows. With the right use of context and configuration, your flows become not just functional, but maintainable and scalable.</p>
<p>As your projects grow, so does the need for better tools—whether it's managing deployments, collaborating with a team, or keeping things running smoothly across environments. That’s where FlowFuse helps you take the next step.</p>
<p><a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Understanding%20Node%2C%20Flow%2C%20Global%2C%20and%20Environment%20Variables%20in%20Node-RED">Start</a> using FlowFuse today and improve your Node-RED experience with built-in tools for scaling, collaboration, and reliability</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/Dashboard 2.0: Milestones, PWA and New ComponentsCheckout all the great content that's been added to Dashboard 2.0 in the past few weeks and the new (in-preview) Vuetify components we've made available in UI Template nodes.2024-04-26T00:00:00ZJoe Pavitt<p>With a new release of Node-RED Dashboard 2.0 we have plenty of new fixes and improvements being added to the project. In this post, we'll deep dive into community contributions, PWA support, new Vuetify components, and the rest of the great work published in this latest release.</p>
<!--more-->
<h2 id="community-contributions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#community-contributions"></a> Community Contributions</h2>
<p>We firstly wanted to take this opportunity to point out a big milestone that we're very proud to see in Node-RED Dashboard 2.0.</p>
<p>This release marks the first time we've had more contributions in a single release from the community, than from FlowFuse employees. I think this is a testament to the community, and a big milestone in validating the success, and popularity, of Dashboard 2.0 in the wider Node-RED community.</p>
<p>So thank you very much <a href="https://github.com/bartbutenaers">@BartButenaers</a>, <a href="https://github.com/Ek1nox">@Ek1nox</a>, <a href="https://github.com/fullmetal-fred">@fullmetal-fred</a> and <a href="https://github.com/cgjgh">@cgjgh</a> for for great efforts and initiative in improving Node-RED Dashboard 2.0.</p>
<p>For anyone else that's interested in contributing to the project, please do reach out, and we'll be happy to help you get started. We also have a <a href="https://dashboard.flowfuse.com/contributing/">Contributing Guide</a> if you want to dive straight in.</p>
<h2 id="progressive-web-app-(pwa)-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#progressive-web-app-(pwa)-support"></a> Progressive Web App (PWA) Support</h2>
<p>The biggest community contribution we saw in this release was the addition of Progressive Web App (PWA) support. This feature was added by <a href="https://github.com/cgjgh">@cgjgh</a>, and allows you to install your Node-RED Dashboard 2.0 applications directly onto your platform, including Windows, iOS and Android.</p>
<p>This work will give your Dashboard's a much more native/natural feel when running on your own machines, and mean you no longer need to go via your browser to access your applications.</p>
<h2 id="new-vuetify-(preview)-components-available" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#new-vuetify-(preview)-components-available"></a> New Vuetify (Preview) Components Available</h2>
<p>Vuetify is the component library on which most of our Dashboard 2.0 components are built. Our core widgets implement the more fundamental UI elements, but that doesn't stop you from building out fully customized interfaces yourself using our <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html">ui-template node</a> with the vast collection of Vuetify components.</p>
<p>Within the <code>ui-template</code> node, we natively support any of the <a href="https://vuetifyjs.com/en/components/all/#containment">core Vuetify components</a>, but Vuetify itself is always evolving and often they release components into their <a href="https://vuetifyjs.com/en/labs/introduction/#what-is-labs">Vuetify Labs</a>.</p>
<p>In their latest releases, a few of the new components have caught our eye as we've seen them regularly requested in Dashboard 2.0. As such, we've now made available the following Vuetify components inside a <code>ui-template</code> node:</p>
<h4 id="number-input-(docs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#number-input-(docs)"></a> Number Input (<a href="https://vuetifyjs.com/en/components/number-inputs/#installation">docs</a>)</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/vuetify-numeric-EJmMmBBhWq-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/vuetify-numeric-EJmMmBBhWq-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Number Input" loading="lazy" decoding="async" src="https://flowfuse.com/img/vuetify-numeric-EJmMmBBhWq-650.jpeg" width="650" height="105" /></picture>
<em>An example v-number-input from Vuetify's component library</em></p>
<p>We do have <a href="https://github.com/FlowFuse/node-red-dashboard/issues/41">plans</a> for this to become a core widget, and will likely introduce this sooner, rather than later, however, in the mean time, you can now use the <code>v-number-input</code> component in a <code>ui-template</code> node to create your own number inputs instead.</p>
<div style="position: relative" id="code-container-49">
<pre class="language-html"><code id="code-49" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-number-input</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>value<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-number-input</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">0</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">watch</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">value</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">payload</span><span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>value<span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /> <span class="token selector">.v-number-input__control .v-btn</span> <span class="token punctuation">{</span><br /> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--v-theme-on-surface<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-49" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="sparkline-(docs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#sparkline-(docs)"></a> Sparkline (<a href="https://vuetifyjs.com/en/components/sparklines/#installation">docs</a>)</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-650.avif 650w, https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-650.webp 650w, https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-650.jpeg 650w, https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Sparkline" loading="lazy" decoding="async" src="https://flowfuse.com/img/vuetify-sparkline-LGw7ngC65i-650.jpeg" width="1300" height="555" /></picture>
<em>An example v-sparkline rendering the output from a ui-slider</em></p>
<p>Sparklines are a great way to visualize data trends in a small space, and we've seen them requested a few times in the past. Now you can use the <code>v-sparkline</code> component in a <code>ui-template</code> node to create your own sparklines.</p>
<p>This will also likely become a standalone node at some point too, possibly as a third-party widget, but for now implementing into a <code>ui-template</code> is very straight forward.</p>
<p>In the following example <code>ui-template</code>, we append any incoming <code>msg.payload</code> to a <code>value</code> array and render the sparkline accordingly.</p>
<div style="position: relative" id="code-container-65">
<pre class="language-html"><code id="code-65" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-sparkline</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>nrdb-ui-sparkline<span class="token punctuation">"</span></span> <span class="token attr-name">:auto-line-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">:fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> <span class="token attr-name">:gradient</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>gradient<span class="token punctuation">"</span></span><br /> <span class="token attr-name">:gradient-direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">'</span>top'<span class="token punctuation">"</span></span> <span class="token attr-name">:line-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token attr-name">:model-value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>value<span class="token punctuation">"</span></span> <span class="token attr-name">:padding</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>8<span class="token punctuation">"</span></span><br /> <span class="token attr-name">:smooth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span> <span class="token attr-name">:stroke-linecap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">'</span>round'<span class="token punctuation">"</span></span> <span class="token attr-name">:type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">'</span>trend'<span class="token punctuation">"</span></span> <span class="token attr-name">auto-draw</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-sparkline</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">data</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">gradient</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'#f72047'</span><span class="token punctuation">,</span> <span class="token string">'#ffd200'</span><span class="token punctuation">,</span> <span class="token string">'#1feaea'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">watch</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function-variable function">msg</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="token selector">.nrdb-ui-sparkline path</span> <span class="token punctuation">{</span><br /> <span class="token property">stroke-dasharray</span><span class="token punctuation">:</span> 0 <span class="token important">!important</span><span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-65" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>There is no limitation on <em>where</em> you can use the sparkline either, we could, for example, add it to a <code>v-data-table</code> to show a sparkline of a particular feature for each row in the table:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-650.avif 650w, https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-650.webp 650w, https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-650.jpeg 650w, https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Data Table with Sparkline" loading="lazy" decoding="async" src="https://flowfuse.com/img/vuetify-data-table-sparkline-xzz9PNNSDP-650.jpeg" width="1300" height="985" /></picture>
<em>An example v-data-table that renders a v-sparkline on each row</em></p>
<p>Here we see the corresponding template for the above <code>v-data-table</code> example:</p>
<div style="position: relative" id="code-container-75">
<pre class="language-html"><code id="code-75" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Provide an input text box to search the content --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-text-field</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Search<span class="token punctuation">"</span></span> <span class="token attr-name">prepend-inner-icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mdi-magnify<span class="token punctuation">"</span></span> <span class="token attr-name">single-line</span> <span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outlined<span class="token punctuation">"</span></span><br /> <span class="token attr-name">hide-details</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-text-field</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-data-table</span> <span class="token attr-name"><span class="token namespace">v-model:</span>search</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token attr-name">:items</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg?.payload<span class="token punctuation">"</span></span> <span class="token attr-name">:headers</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>headers<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>header.pingvalues</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> Ping History<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>item.ping</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> ms<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>item.pingvalues</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-sparkline</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item.pingValues<span class="token punctuation">"</span></span> <span class="token attr-name">:gradient</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>['#42b3f4', '#42b3f400']<span class="token punctuation">"</span></span><br /> <span class="token attr-name">:line-width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span> <span class="token attr-name">gradientDirection</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>top<span class="token punctuation">"</span></span> <span class="token attr-name">:smooth</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">:fill</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-data-table</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">search</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">'id'</span><span class="token punctuation">,</span> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'ID'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">'ping'</span><span class="token punctuation">,</span> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'Ping'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token string">'pingvalues'</span><span class="token punctuation">,</span> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">'Ping History'</span> <span class="token punctuation">}</span><br /> <span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-75" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="treeview-(docs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#treeview-(docs)"></a> Treeview (<a href="https://vuetifyjs.com/en/components/treeview/#installation">docs</a>)</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-650.avif 650w, https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-650.webp 650w, https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-650.jpeg 650w, https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Treeview" loading="lazy" decoding="async" src="https://flowfuse.com/img/vuetify-treeview-LoCGQB6ngA-650.jpeg" width="1300" height="1094" /></picture>
<em>An example v-treeview from Vuetify Lab's component library</em></p>
<p>The <code>v-treeview</code> component is a great way to visualize hierarchical data in a tree-like structure. We've seen this requested a few times in the past, and now you can use the <code>v-treeview</code> component in a <code>ui-template</code> node to create your own treeviews.</p>
<p>There is still scope for this to, one day, become a core or third party widget, but in the mean time, it's very easy to get this up and running in a <code>ui-template</code> node.</p>
<p>The Treeview example, and other examples above are available in this sample flow:</p>
<iframe width="100%" height="340px" src="https://flows.nodered.org/flow/0ac4d82aaf97409cb0dce9812cfa214c/share?height=300" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<h2 id="other-highlights" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#other-highlights"></a> Other Highlights</h2>
<p>Whilst the above are the main highlights of this release, we've also had a number of other smaller improvements and fixes that have been added to the project. These include:</p>
<ul>
<li>UI Radio Group - Dynamic radio options in <a href="https://github.com/FlowFuse/node-red-dashboard/pull/765">#765</a></li>
<li>UI Notification - Notification output & output msg when button clicked in <a href="https://github.com/FlowFuse/node-red-dashboard/pull/766">#766</a></li>
<li>UI Dropdown - Clear dropdown selection in <a href="https://github.com/FlowFuse/node-red-dashboard/pull/775">#775</a></li>
<li>UI Button - Add "Emulate Click" option in <a href="https://github.com/FlowFuse/node-red-dashboard/pull/783">#783</a></li>
</ul>
<p>You can see the full list of changes in the <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v1.8.0">1.8.0 Release Notes</a>.</p>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/dashboard-milestones-pwa-new-components/#follow-our-progress"></a> Follow our Progress</h2>
<p>New features and improvements are coming to Node-RED Dashboard 2.0 every week, if you're interested in what we have lined up, or want to contribute yourself, then you can track the work we have lined up on our GitHub Projects:</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/5">Dashboard 1.0 Feature Parity Tracker</a></li>
</ul>
<p>If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/How to Build An Application With Node-RED Dashboard 2.0A step-by-step guide to building a personalized, secure, and fully functional application with Dashboard 2.0.2024-04-25T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>If you're new to Dashboard 2.0, refer to our blog post <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started with Dashboard 2.0</a> to install and get things started.</p>
<h2 id="installing-flowfuse-user-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#installing-flowfuse-user-addon"></a> Installing Flowfuse user addon</h2>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#exploring-the-flowfuse-user-addon">Exploring the FlowFuse User Addon</a> and make sure to install it.</p>
<p>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 <a href="https://flowfuse.com/docs/user/instance-settings/#flowfuse-user-authentication">documentation</a> and make sure that it is enabled.</p>
<h2 id="building-task-management-application" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#building-task-management-application"></a> Building Task Management application</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-task-management-system-YgMVnDC_-u-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the task management system built with Node-RED Dashboard 2.0" alt=""Screenshot of the Todo application built with Node-RED Dashboard 2.0"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-2-task-management-system-YgMVnDC_-u-800.webp" width="800" height="450" /></picture></p>
<p>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.</p>
<h3 id="building-a-form-to-submit-tasks" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#building-a-form-to-submit-tasks"></a> Building a Form to Submit Tasks</h3>
<ol>
<li>Drag an <strong>ui-form</strong> widget onto the canvas.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-page1-configuration-isPELymDUN-575.avif 575w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-page1-configuration-isPELymDUN-575.webp 575w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying 'new task' page configurations" alt=""Screenshot displaying 'new task' page configurations"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-2-page1-configuration-isPELymDUN-575.jpeg" width="575" height="826" /></picture></p>
<ol start="3">
<li>Click on the <strong>ui-form</strong> widget to add form elements such as title, description, due date, and priority.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-task-submission-form-crOsKAhLIC-1070.avif 1070w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-task-submission-form-crOsKAhLIC-1070.webp 1070w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of ui widget form configuration" alt=""Screenshot of ui widget form configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-2-task-submission-form-crOsKAhLIC-1070.jpeg" width="1070" height="824" /></picture></p>
<h3 id="storing-tasks-in-the-global-context" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#storing-tasks-in-the-global-context"></a> Storing Tasks in the Global Context</h3>
<p>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.</p>
<ol>
<li>Drag a <strong>function</strong> node onto the canvas</li>
<li>Paste the below code in the <strong>function</strong> node.</li>
</ol>
<div style="position: relative" id="code-container-71">
<pre class="language-javascript"><code id="code-71" class="language-javascript"><span class="token comment">// Retrieve the existing tasks from the global context or initialize an empty array if none exists</span><br /><br /><span class="token keyword">let</span> tasks <span class="token operator">=</span> global<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'tasks'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// 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.</span><br /><br />tasks<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token operator">...</span>msg<span class="token punctuation">.</span>payload<span class="token punctuation">,</span><br /> <span class="token operator">...</span><span class="token punctuation">{</span><br /> user<span class="token operator">:</span> msg<span class="token punctuation">.</span>_client<span class="token punctuation">.</span>user <span class="token comment">// Assign the user object to the task</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Update the 'tasks' variable in the global context with the modified tasks array</span><br /><br />global<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'tasks'</span><span class="token punctuation">,</span> tasks<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-71" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the <strong>ui-form</strong> widget’s output to the <strong>function</strong> node’s input.</li>
</ol>
<h3 id="displaying-notification-on-successful-task-submission" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#displaying-notification-on-successful-task-submission"></a> Displaying notification on successful task submission</h3>
<ol>
<li>Drag a <strong>change</strong> node onto the canvas and set <code>msg.payload</code> to the confirmation message you want to display on successful task submission.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-setting-payload-for-notification-_0-VQdG9p1-571.avif 571w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-setting-payload-for-notification-_0-VQdG9p1-571.webp 571w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="screenshot of the change node setting payload for notification" alt=""screenshot of the change node setting payload for notification"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-setting-payload-for-notification-_0-VQdG9p1-571.jpeg" width="571" height="459" /></picture></p>
<ol start="2">
<li>Drag an <strong>ui-notification</strong> onto the canvas select <strong>ui-base</strong> and set the position to "center".</li>
<li>Connect the <strong>ui-form</strong> widget’s output to the <strong>change</strong> node’s input and the <strong>change</strong> node’s output to the <strong>ui-notification</strong> widget's input.</li>
</ol>
<h3 id="retrieving-and-filtering-tasks" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#retrieving-and-filtering-tasks"></a> Retrieving and Filtering Tasks</h3>
<p>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.</p>
<ol>
<li>Drag a <strong>ui-event</strong> widget onto the canvas and select <strong>ui-base</strong> for it. The <strong>ui-event</strong> will enable us to display updated tasks on the table without the need for polling, as it triggers when the page reloads or changes.</li>
<li>Drag a <strong>change</strong> node onto the canvas and set <code>msg.payload</code> to <code>global.tasks</code>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-retriving-global-context-data-ojpp6n17Fr-588.avif 588w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-retriving-global-context-data-ojpp6n17Fr-588.webp 588w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the change setting retriving global context and setting to msg.payload" alt=""Screenshot of the change setting retriving global context and setting to msg.payload"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-change-node-retriving-global-context-data-ojpp6n17Fr-588.jpeg" width="588" height="472" /></picture></p>
<ol start="3">
<li>Drag a <strong>function</strong> node onto the canvas and paste the below code into it.</li>
</ol>
<div style="position: relative" id="code-container-132">
<pre class="language-javascript"><code id="code-132" class="language-javascript"><span class="token comment">// Filter the payload array of tasks to include only those tasks associated with the currently logged in user.</span><br /><br />msg<span class="token punctuation">.</span>payload <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">task</span><span class="token punctuation">)</span> <span class="token operator">=></span> task<span class="token punctuation">.</span>user<span class="token punctuation">.</span>userId <span class="token operator">===</span> msg<span class="token punctuation">.</span>_client<span class="token punctuation">.</span>user<span class="token punctuation">.</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token comment">// Return the modified message object containing the filtered tasks.</span><br /><br /><span class="token keyword">return</span> msg<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-132" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="4">
<li>Connect the <strong>ui-event</strong> widget’s output to the <strong>change</strong> node’s input and the <strong>change</strong> nodes’ output to the <strong>function</strong> node’s input.</li>
</ol>
<h3 id="enabling-client-constraint-for-ui-template" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#enabling-client-constraint-for-ui-template"></a> Enabling client constraint for ui-template</h3>
<p>Before we begin building our table to display tasks, we need to enable access to client constraints for the <strong>ui-template</strong> 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.</p>
<p>If you have experience with Node-RED Dashboard 1.0, you may recall that these client constraints were only available for <strong>ui-control</strong> and <strong>ui-notification</strong> widgets but in Dashboard 2.0 you can enable it for any widget.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-ff-auth-tab-2h68sXsxlc-1851.avif 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-2-ff-auth-tab-2h68sXsxlc-1851.webp 1851w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying FF Auth tab" alt=""Screenshot displaying FF Auth tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-2-ff-auth-tab-2h68sXsxlc-1851.jpeg" width="1851" height="904" /></picture></p>
<ol>
<li>Navigate to the Dashboard 2.0 sidebar and select the top-right "FF Auth" Tab</li>
<li>In the "Accept Client Constraints" option, you'll see Dashboard 2.0 widgets where this option is by default enabled for <strong>ui-notification</strong> and <strong>ui-control</strong>, enable it for <strong>ui-template</strong> as well.</li>
</ol>
<h3 id="creating-a-table-and-displaying-the-task" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#creating-a-table-and-displaying-the-task"></a> Creating a table and displaying the task</h3>
<p>In this section, we will build an interactive table using <strong>ui-template</strong> and <a href="https://vuetifyjs.com/en/components/all/">vuetify component</a>. 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 <strong>ui-template</strong> widget.</p>
<ol>
<li>Drag an <strong>ui-template</strong> widget onto the canvas</li>
<li>Create a new <strong>ui-page</strong> and <strong>ui-group</strong> 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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-template-widget-MbMU1WdjlS-579.avif 579w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-template-widget-MbMU1WdjlS-579.webp 579w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying ui-template widget with code for building table for displaying task" alt=""Screenshot displaying ui-template widget with code for building table for displaying task"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-template-widget-MbMU1WdjlS-579.jpeg" width="579" height="827" /></picture></p>
<ol start="3">
<li>Paste the below code into the widget, If you're new to Vue.js, rest assured I've included helpful comments for clarity.</li>
</ol>
<div style="position: relative" id="code-container-192">
<pre class="language-html"><code id="code-192" class="language-html">` <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Input field for searching tasks --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-text-field</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Search<span class="token punctuation">"</span></span> <span class="token attr-name">prepend-inner-icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mdi-magnify<span class="token punctuation">"</span></span> <span class="token attr-name">single-line</span> <span class="token attr-name">variant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>outlined<span class="token punctuation">"</span></span><br /> <span class="token attr-name">hide-details</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-text-field</span><span class="token punctuation">></span></span><br /> <br /> <span class="token comment"><!-- Data table to display tasks --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-data-table</span> <span class="token attr-name">:search</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<span class="token punctuation">"</span></span> <span class="token attr-name">:items</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg?.payload<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Custom header for the "current" column --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>header.current</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Center-Aligned<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /> <span class="token comment"><!-- Template for the "priority" column --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>item.priority</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Display priority icon if it exists --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-icon</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item.priority<span class="token punctuation">"</span></span> <span class="token attr-name">icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>mdi-alert<span class="token punctuation">"</span></span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>red<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-icon</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /> <span class="token comment"><!-- Template for the "user" column --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>item.user</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Display user avatar and username --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>user<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- User avatar --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">:src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item.user.image<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>24<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /> <span class="token comment"><!-- Username --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>{{ item.user.username }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /> <span class="token comment"><!-- Template for the "due" column --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span> <span class="token attr-name"><span class="token namespace">v-slot:</span>item.due</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{ item }<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Calculate and display the number of days between due date and current date --></span><br /> {{ daysBetween(item.due, new Date()) }} Days<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-data-table</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Search input field model</span><br /> search<span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> methods<span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Method to calculate the number of days between two dates</span><br /> <span class="token function">daysBetween</span><span class="token punctuation">(</span><span class="token parameter">date1<span class="token punctuation">,</span> date2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Calculate the difference in days</span><br /> <span class="token keyword">const</span> oneDay <span class="token operator">=</span> <span class="token number">24</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">;</span> <span class="token comment">// hours*minutes*seconds*milliseconds</span><br /> <span class="token keyword">const</span> firstDate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>date1<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> secondDate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span>date2<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> diffDays <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">round</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span><span class="token punctuation">(</span>firstDate <span class="token operator">-</span> secondDate<span class="token punctuation">)</span> <span class="token operator">/</span> oneDay<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> diffDays<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name">scoped</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /> <span class="token comment">/* Styling for user avatar and username */</span><br /> <span class="token selector">.user</span> <span class="token punctuation">{</span><br /> display<span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br /> gap<span class="token punctuation">:</span> 5px<span class="token punctuation">;</span> <span class="token comment">/* Gap between avatar and username */</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">/* Styling for user avatar */</span><br /> <span class="token selector">.user img</span> <span class="token punctuation">{</span><br /> width<span class="token punctuation">:</span> 24px<span class="token punctuation">;</span> <span class="token comment">/* Set width of user avatar */</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span><br /></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-192" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="3">
<li>Connect the <strong>ui-template</strong> widget's input to the <strong>function</strong> node's output ( function node which we have added to filter tasks based on user ).</li>
</ol>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#deploying-the-flow"></a> Deploying the flow</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-task-management-application-flow-eEocwn7Su0-1852.avif 1852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-an-application-with-dashboard-task-management-application-flow-eEocwn7Su0-1852.webp 1852w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying Node-RED flow of Task management system" alt=""Screenshot displaying Node-RED flow of Task management system"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-an-application-with-dashboard-task-management-application-flow-eEocwn7Su0-1852.jpeg" width="1852" height="898" /></picture></p>
<ol>
<li>Deploy the flow by clicking the top right Deploy button.</li>
<li>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.</li>
<li>Login with the registered FlowFuse account username and password.</li>
</ol>
<p>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.</p>
<h2 id="next-step" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/how-to-build-an-application-with-node-red-dashboard-2/#next-step"></a> Next step</h2>
<p>If you want to enhance this simple application by adding more features, consider the following resources:</p>
<ul>
<li>
<p><a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">Webinar</a> - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/">Displaying logged-in users on Dashboard 2.0</a> - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/">How to Build an Admin Dashboard with Node-RED Dashboard 2.0</a> - This detailed guide demonstrates how to build a secure admin page in Node-RED Dashboard 2.0.</p>
</li>
<li>
<p><a href="https://flowfuse.com/blueprints/flowfuse-dashboard/multi-user-dashboard/">Multi-User Dashboard for Ticket/Task Management</a> 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.</p>
</li>
</ul>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20Build%20An%20Application%20With%20Node-RED%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-dedicated/Introducing FlowFuse DedicatedThe Next Level in Node-RED Solutions2024-04-22T00:00:00Z<p>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.</p>
<!--more-->
<p>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.</p>
<p>Here’s what sets FlowFuse Dedicated apart:</p>
<ul>
<li><strong>Single-Tenant FlowFuse Platform:</strong> Enjoy the exclusivity of a dedicated environment, ensuring that your operations are isolated, secure, and tailored to your specific needs.</li>
<li><strong>SaaS-like Efficiency:</strong> 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.</li>
<li><strong>Regional Deployment Flexibility:</strong> 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.</li>
<li><strong>Enterprise-Grade Features:</strong> Enjoy access to all enterprise features and configurations as your own FlowFuse Administrator, enabling you to manage multiple teams effectively.</li>
<li><strong>Comprehensive Compliance:</strong> Meet complex compliance standards with ease, thanks to full data and source code IP isolation.</li>
</ul>
<p>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. <a href="https://flowfuse.com/dedicated/">Learn more about FlowFuse Dedicated</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/FlowFuse Gears Up for Hannover MesseMeet the team behind FlowFuse and contributors to Node-RED2024-04-11T00:00:00Z<p>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.</p>
<p><strong>Do you need a pass? We can help with that as well.</strong></p>
<!--more-->
<h2 id="understanding-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#understanding-node-red"></a> Understanding Node-RED</h2>
<p>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.</p>
<h2 id="scaling-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#scaling-with-flowfuse"></a> Scaling with FlowFuse</h2>
<p>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:</p>
<ul>
<li>Operations and Maintenance: Get a consolidated view of your Node-RED instances, track their health, and ensure smooth operation.</li>
<li>Developer Velocity: Enhance development efficiency with streamlined updates, remote access, and version control for Node-RED deployments.</li>
<li>Security and Compliance: Integrate Node-RED with your existing security systems, implement detailed user permissions, and maintain audit logs to satisfy industry regulations.</li>
</ul>
<h2 id="experience-flowfuse-at-hannover-messe" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#experience-flowfuse-at-hannover-messe"></a> Experience FlowFuse at Hannover Messe</h2>
<h3 id="come-see-us-at-our-booth-for%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#come-see-us-at-our-booth-for%3A"></a> Come see us at our booth for:</h3>
<ul>
<li>Live demonstrations showcasing how FlowFuse enhances your Node-RED applications.</li>
<li>Discussions about tackling common challenges in industrial data management.</li>
<li>A chance to connect with our team and learn how we can support your goals.</li>
</ul>
<h2 id="let's-connect!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#let's-connect!"></a> Let's Connect!</h2>
<p>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!</p>
<h2 id="get-your-free-pass-to-hannover-messe" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/flowfuse-at-hannover-messe-node-red/#get-your-free-pass-to-hannover-messe"></a> Get Your Free Pass to Hannover Messe</h2>
<p>Interested in attending Hannover Messe? Click <a href="https://www.hannovermesse.de/en/application/registration/direct-entry-tickets-passes?code=hXWhs">here</a> to claim your free pass to the event.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-multiplayer/Node-RED Multiplayer modeAn update on our work to bring concurrent editing to Node-RED2024-04-10T00:00:00ZNick O'Leary<p>Last month I <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/">wrote</a> about what users
can expect with Node-RED 4.0. One of the features I highlighted was improving the
concurrent editing experience. With the recent <a href="https://discourse.nodered.org/t/node-red-4-0-0-beta-2-released/87026">beta release</a> of Node-RED, the first
steps on that journey are now available to try.</p>
<p>In this post, I'll cover what has been done so far and what we've got planned.</p>
<!--more-->
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr-multiplayer-l5n4P8veyh-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr-multiplayer-l5n4P8veyh-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot of Node-RED with additional user icons in the header" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr-multiplayer-l5n4P8veyh-650.jpeg" width="650" height="308" /></picture></p>
<figcaption>Screenshot of Node-RED with additional user icons in the header</figcaption>
<p>Clicking on their icon will show a little information box about <em>where</em> 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.</p>
<h3 id="next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-multiplayer/#next-steps"></a> Next steps</h3>
<p>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.</p>
<p>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.</p>
<h3 id="trying-it-out-on-flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-multiplayer/#trying-it-out-on-flowfuse-cloud"></a> Trying it out on FlowFuse Cloud</h3>
<p>I mentioned earlier this feature has to be enabled. For a local install, details on how to do this are in the <a href="https://discourse.nodered.org/t/node-red-4-0-0-beta-2-released/87026#introducing-multiplayer-mode-2">release forum post</a>.</p>
<p>If you want to give it a go now, we already have Node-RED v4-beta.2 available to <a href="https://app.flowfuse.com/">try on FlowFuse Cloud</a> - where the multiplayer feature is pre-enabled whilst it's in beta mode.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/How to Build an Admin Dashboard with Node-RED Dashboard 2.0A guide to building an Admin Dashboard in Node-RED with Dashboard 2.02024-04-08T00:00:00ZSumit Shinde<p>Managing and analyzing increasing amounts of data becomes crucial for organizations. Dashboard 2.0 and Node-RED help organizations access the data, normalize it, and visualize it. But what about controlling who can access what data? That's where an admin-only page comes in. Now With Node-RED Dashboard 2.0, we can also create robust and secure admin-only pages easily. In this guide, we'll provide you with step-by-step instructions to Build an Admin-only page with Node-RED Dashboard 2.0.</p>
<!--more-->
<p>If you're new to Dashboard 2.0, refer to our blog post <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started with Dashboard 2.0</a> to install and get things started.</p>
<h2 id="enabling-flowfuse-user-authentication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#enabling-flowfuse-user-authentication"></a> Enabling FlowFuse User Authentication</h2>
<p>Before proceeding further, let’s enable FlowFuse user authentication. This step adds an extra layer of protection to our dashboard by adding a login page that restricts access exclusively to registered FlowFuse users. Additionally, it further simplifies the process for the FlowFuse Multiuser addon to track and access logged-in user's data on the dashboard.</p>
<p>For more information, refer to the <a href="https://flowfuse.com/docs/user/instance-settings/#flowfuse-user-authentication">documentation</a> and ensure that it is enabled.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-instance-setting-15VTn1N4dR-1850.avif 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-instance-setting-15VTn1N4dR-1850.webp 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security." alt=""Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security.
"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-instance-setting-15VTn1N4dR-1850.jpeg" width="1850" height="893" /></picture></p>
<h2 id="exploring-flowfuse-multiuser-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#exploring-flowfuse-multiuser-addon"></a> Exploring FlowFuse Multiuser Addon</h2>
<p>The FlowFuse Multiuser Addon is a plugin developed for Dashboard 2.0 to access logged-in user data on the dashboard. To install and understand how the FlowFuse Multiuser Addon works, refer to <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#enabling-flowfuse-user-authentication">Exploring the FlowFuse User Addon </a></p>
<h2 id="storing-a-list-of-admin-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#storing-a-list-of-admin-users"></a> Storing a list of Admin users</h2>
<p>Before we start building the admin-only page We need to store a list of admin users somewhere so that we can later display the admin-only page to those users only, For this guide we will store the admin list in the global context.</p>
<ol>
<li>Drag an inject node onto the canvas.</li>
<li>Drag the 'change' node onto the canvas and set <code>global.admins</code> to a JSON array containing the usernames of admin users. This will store the created admin list in our Node-RED global context.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-storing-adminlist-to-global-contenxt-Ou8Jy4Kswy-590.avif 590w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-storing-adminlist-to-global-contenxt-Ou8Jy4Kswy-590.webp 590w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node which which stores list of admins username in global context" alt=""Screenshot displaying the change node which which stores list of admins username in global context"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-storing-adminlist-to-global-contenxt-Ou8Jy4Kswy-590.jpeg" width="590" height="428" /></picture></p>
<ol start="3">
<li>Connect the inject node’s output to the change node’s input.</li>
<li>To store the list in a global context, click the inject node’s button once you've deployed the flow.</li>
</ol>
<h2 id="building-an-admin-only-page" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#building-an-admin-only-page"></a> Building an Admin-only page</h2>
<p>Now, let's proceed with the practical steps to implement the admin-only page:</p>
<ol>
<li>Create a new page in Dashboard 2.0, where we will display sensitive data that we want to hide from regular users, this page will be our admin page.</li>
<li>Drag an event node on the canvas, then click on it, and select the UI base that contains your all pages including the admin page</li>
<li>Drag a switch node on the canvas, and add two conditions, one to check whether the user’s username is contained in the admin list or a second for otherwise, see the below image.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-switch-node-checking-page-viewer-isadmin-xTG33cCtQe-607.avif 607w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-switch-node-checking-page-viewer-isadmin-xTG33cCtQe-607.webp 607w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the switch node which checks whether the logged-in user's username is contained in the admin list or not" alt=""Screenshot displaying the switch node which checks whether the logged-in user's username is contained in the admin list or not"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-switch-node-checking-page-viewer-isadmin-xTG33cCtQe-607.jpeg" width="607" height="452" /></picture></p>
<ol start="4">
<li>Drag two change nodes onto the canvas, Configure the first change node to show the admin page by setting <code>msg.payload</code> as <code>{"pages":{"show":["Admin View"]}}</code>, and the second change node to hide the admin page by setting the payload as: <code>{"pages":{"hide":["Admin View"]}}</code>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-showing-page-jelhhNzj-l-629.avif 629w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-showing-page-jelhhNzj-l-629.webp 629w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node which contains payload to show admin page" alt=""Screenshot displaying the change node which contains payload to show admin page"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-showing-page-jelhhNzj-l-629.jpeg" width="629" height="443" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-hidding-page-mThgupQgtJ-624.avif 624w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-hidding-page-mThgupQgtJ-624.webp 624w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the change node which contains payload to display admin page" alt=""Screenshot displaying the change node which contains payload to hide admin page"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-change-node-for-hidding-page-mThgupQgtJ-624.jpeg" width="624" height="430" /></picture></p>
<ol start="5">
<li>Connect the first change node's input to the switch node's first output and the second change node's input to the switch node's second output.</li>
<li>Drag a ui-control widget onto the canvas, then click on it and select ui-base which includes all your pages including the admin page.</li>
<li>Finally, connect both change node’s outputs to the ui-control’s input.</li>
</ol>
<h2 id="hidding-admin-only-page-by-default" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#hidding-admin-only-page-by-default"></a> Hidding Admin only page by default</h2>
<p>To hide an admin-only page by default to ensure regular users don't accidentally land on the admin-only page the following steps are needed.</p>
<ol>
<li>Go to the Dashboard 2.0 sidebar, and select the layout tab.</li>
<li>Locate the admin-only page and click on the edit icon next to it.</li>
<li>Set visibility as "hidden".</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-admin-only-page-configuration-TNaR835c2u-579.avif 579w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-admin-only-page-configuration-TNaR835c2u-579.webp 579w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying admin-only page configuration" alt=""Screenshot displaying admin-only page configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-admin-only-page-configuration-TNaR835c2u-579.jpeg" width="579" height="780" /></picture></p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#deploying-the-flow"></a> Deploying the flow</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-editior-8_CuNXWu-A-1844.avif 1844w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-editior-8_CuNXWu-A-1844.webp 1844w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the FlowFuse Editor with flow of admin-only page" alt=""Screenshot displaying the FlowFuse Editor with flow of admin-only page"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-flowfuse-editior-8_CuNXWu-A-1844.jpeg" width="1844" height="898" /></picture></p>
<ol>
<li>With your flow updated to include the above, click the "Deploy" button in the top-right of the Node-RED Editor.</li>
<li>Navigate to <code>https://<your-instance-name>.flowfuse.cloud/dashboard</code>.</li>
<li>When you visit the page for the first time, you'll need to log in with your FlowFuse username and password or through Single-Sign on.</li>
</ol>
<p>Now, if your username is added to the list of admin usernames stored in the global context, you will be able to see the admin-only page.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-normal-users-xwEM0wBufn-1826.avif 1826w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-normal-users-xwEM0wBufn-1826.webp 1826w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the Dashboard view of normal users" alt=""Screenshot displaying the Dashboard view of normal users"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-normal-users-xwEM0wBufn-1826.jpeg" width="1826" height="882" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-admin-users-k7Ths8LDj8-1824.avif 1824w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-admin-users-k7Ths8LDj8-1824.webp 1824w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the Dashboard view of admin users" alt=""Screenshot displaying the Dashboard view of admin users"" loading="lazy" decoding="async" src="https://flowfuse.com/img/building-admin-panel-node-red-dashboard-2-dashboard-view-for-admin-users-k7Ths8LDj8-1824.jpeg" width="1824" height="892" /></picture></p>
<h2 id="next-step" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/building-an-admin-panel-in-node-red-with-dashboard-2/#next-step"></a> Next step</h2>
<p>If you want to learn more about FlowFuse multiuser addon and personalize the multiuser dashboard. we do have many other resources, please refer to them to learn more.</p>
<ul>
<li><a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">Webinar</a> - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/">Personalised Multi-user Dashboards with Node-RED Dashboard 2.0</a> - This article explores the process of building multi-user Dashboards secured with FlowFuse Cloud.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/">Displaying logged-in users on Dashboard 2.0</a> - This detailed guide demonstrates how to display logged-in users on Dashboard 2.0 which using the FlowFuse Multiuser addon and FlowFuse.</li>
<li><a href="https://flowfuse.com/blueprints/flowfuse-dashboard/multi-user-dashboard/">Multi-User Dashboard for Ticket/Task Management</a> blueprint, which allows you to utilize templates to develop Personalize multi-user dashboard quickly.</li>
</ul>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=How%20to%20Build%20an%20Admin%20Dashboard%20with%20Node-RED%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/Role-Based Access for your Node-RED applicationsCollaboration and Security in Node-RED with Role-Based Access Control with FlowFuse.2024-04-03T00:00:00Z<p>Role-based access Control (RBAC) is a crucial component of modern software platforms. It provides granular control over access rights and enhances security. Integrating RBAC into the FlowFuse platform brings this level of sophistication to Node-RED, allowing users to manage their environments with precision and confidence. This blog will explore how FlowFuse's RBAC system improves both security and user experience within the Node-RED ecosystem.</p>
<!--more-->
<h2 id="roles-overview" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/#roles-overview"></a> Roles Overview</h2>
<p>FlowFuse offers a variety of roles that cater to different levels of access within the Node-RED environment, ensuring that users can tailor their platform according to their needs. These roles include:</p>
<ol>
<li>Owner - The highest level of access with full control over team settings, instances, and features.</li>
<li>Member - Functions similarly to Owner, but inhibits the abilities to change instance states and manage members.</li>
<li>Viewer - Allows users to visualize and see flows and dashboards. This is great for collaboration across multi tenant teams.</li>
<li>Dashboard Only - Designed for end users that will interact with Dashboards, but don't have a reason to see or edit flows.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/role-based-access-control-for-node-red-flowfuse-csI8Czl6dG-585.avif 585w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/role-based-access-control-for-node-red-flowfuse-csI8Czl6dG-585.webp 585w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Role Based Access Control For Node-RED with FlowFuse" alt=""Role Based Access Control For Node-RED with FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/role-based-access-control-for-node-red-flowfuse-csI8Czl6dG-585.jpeg" width="585" height="438" /></picture></p>
<p>For further details see <a href="https://flowfuse.com/docs/user/team/#role-based-access-control">documentation</a> for full granularity of roles.</p>
<h2 id="instance-protection-mode" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/#instance-protection-mode"></a> Instance Protection Mode</h2>
<p>A useful feature of FlowFuse's RBAC system is the Instance Protection Mode. This mode adds an additional layer of security by locking down critical Node-RED instances, preventing unauthorized modifications to configuration and nodes. The protection mode is particularly useful in production environments and areas in which need an extra layer of protection for any unintentional updates or changes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/protect-instance-node-red-with-flowfuse-VNjW_myFBL-551.avif 551w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/protect-instance-node-red-with-flowfuse-VNjW_myFBL-551.webp 551w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Protect Node-RED instance from change with Instance Protection Mode" alt=""Protect Node-RED instance from change with Instance Protection Mode"" loading="lazy" decoding="async" src="https://flowfuse.com/img/protect-instance-node-red-with-flowfuse-VNjW_myFBL-551.jpeg" width="551" height="405" /></picture></p>
<h2 id="the-impact-on-user-experience" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/#the-impact-on-user-experience"></a> The Impact on User Experience</h2>
<p>FlowFuse's RBAC system significantly enhances the user experience for Node-RED users by providing a secure, customizable, and controlled environment. Teams can collaborate more efficiently and safely, knowing that access is properly managed and critical systems are protected.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/#conclusion"></a> Conclusion</h2>
<p>FlowFuse's integration of RBAC into the Node-RED ecosystem represents a significant enhancement in how users can interact with, manage, and secure their Node-RED instances. By offering detailed control over access rights and introducing features like Instance Protection Mode, FlowFuse not only secures Node-RED environments but also greatly improves their manageability and the overall user experience. With RBAC, users can now tailor their Node-RED platforms according to their specific needs while ensuring that security and collaboration are at the forefront of their operations.</p>
<p>For more information on how to implement and utilize Role-Based Access Control within FlowFuse, please refer to our <a href="https://flowfuse.com/docs/user/team/#role-based-access-control">documentation</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/Displaying logged in user on Node-RED Dashboard 2.0Step-by-Step Beginner's Guide to Displaying logged in User on Node-RED Dashboard 2.02024-04-03T00:00:00ZSumit Shinde<p>About a month ago, a powerful solution became available to the Node-RED community to deal with users and allow multiple to interact with the same dashboard in a personalized manner. It's called the Multli user Dashboard for Node-RED. In this guide, we will provide a step-by-step guide to show you how to secure your dashboard and access and display logged in user information on Dashboard 2.0.</p>
<!--more-->
<p>If you're new to Dashboard 2.0, refer to our blog post <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting Started with Dashboard 2.0</a></p>
<h2 id="enabling-flowfuse-user-authentication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#enabling-flowfuse-user-authentication"></a> Enabling FlowFuse User Authentication</h2>
<p>Before we display logged-in user data on the dashboard, first we need to set up a login mechanism with FlowFuse for the dashboard. This simplifies securing Node-RED Dashboards and provides contextual user data within the Dashboard itself for who is logged in.</p>
<ol>
<li>Navigate to the Instance "settings".</li>
<li>Select the "Security" tab.</li>
<li>Enable “FlowFuse User Authentication”</li>
</ol>
<p>Now, the first time you visit the dashboard, you'll need to log in with your registered FlowFuse username and password</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/displaying-logged-in-user-flowfuse-instance-setting-15VTn1N4dR-1850.avif 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/displaying-logged-in-user-flowfuse-instance-setting-15VTn1N4dR-1850.webp 1850w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security." alt=""Screenshot displaying the configuration settings within the FlowFuse instance, enabling user authentication for enhanced security."" loading="lazy" decoding="async" src="https://flowfuse.com/img/displaying-logged-in-user-flowfuse-instance-setting-15VTn1N4dR-1850.jpeg" width="1850" height="893" /></picture></p>
<h2 id="exploring-the-flowfuse-user-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#exploring-the-flowfuse-user-addon"></a> Exploring the FlowFuse User Addon</h2>
<p>The FlowFuse User Addon is a plugin developed for Dashboard 2.0, leveraging the FlowFuse API to retrieve information about logged in user.</p>
<h3 id="installing-flowfuse-user-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#installing-flowfuse-user-addon"></a> Installing Flowfuse user addon</h3>
<ol>
<li>Click the Node-RED Settings (top-right)</li>
<li>Click "Manage Palette"</li>
<li>Switch to the "Install" tab</li>
<li>Search for <code>@flowfuse/node-red-dashboard-2-user-addon</code></li>
<li>Click "Install"</li>
</ol>
<h3 id="how-it-works" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#how-it-works"></a> How it Works</h3>
<p>In this addon, user information is attached to the <code>msg</code> emitted by Dashboard 2.0 nodes. This user information object is attached as <code>msg._client.user</code>. Below is an example of how that object looks:</p>
<pre><code>{
"userId": "", // unique identifier for the user
"username": "", // FlowFuse Username
"email": "", // E-Mail Address connected to their FlowFuse account
"name": "", // Full Name
"image": "" // User Avatar from FlowFuse
}
</code></pre>
<p>Behind the scenes, the user addon is appending the user object to the <code>msg</code>, via the SocketIO auth option. We make the socketio object available via a computed <a href="https://dashboard.flowfuse.com/contributing/guides/state-management.html#setup-store">setup</a> object, this means that we can also access user data in any ui-template widget with ``, in the <code><template></code>, or <code>this.setup.socketio.auth.user</code>, in the JS.</p>
<p>When running Node-RED Dashboard 2.0 on FlowFuse, you'll have a new tab available in the "Dashboard 2.0" sidebar in the Node-RED Editor, you just have to navigate to the "FF Auth" tab and you’ll see two options.</p>
<p><strong>Option 1: Include Client Data</strong></p>
<p>By default, this option is enabled. When this option is enabled, an object with user information will be added to the “msg” emitted by any widget of the Node-RED Dashboard 2.0.</p>
<p><strong>Option 2: Accept Client Constraints</strong></p>
<p>A feature that ensures messages are specifically targeted to individual clients, which enhances the precision and security of data transmission within the platform. It determines by enabling the nodes option in the FF Auth tab whether the enabled node type will utilize client data, such as socketid, and restrict communications to only that client.</p>
<p>For example, consider a manufacturing facility where each production line has its own monitoring system. With this feature enabled, data from sensors on Production Line A will only be sent to the monitoring system designated for Production Line A. This ensures that data remains isolated and relevant to each specific area of operation, maintaining organizational efficiency and security.</p>
<p><em>Note: Please note that Multi-User Addons can only be used by our Teams and Enterprise Self-Hosted customers. Upon request, we provide all required configurations to get started.</em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/displaying-logged-in-user-ff-auth-tab-mVsXkmS-jw-1846.avif 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/displaying-logged-in-user-ff-auth-tab-mVsXkmS-jw-1846.webp 1846w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot displaying the FlowFuse Muti-user addon option" alt=""Screenshot displaying the FlowFuse Muti-user addon option"" loading="lazy" decoding="async" src="https://flowfuse.com/img/displaying-logged-in-user-ff-auth-tab-mVsXkmS-jw-1846.jpeg" width="1846" height="888" /></picture></p>
<h2 id="displaying-logged-in-user-on-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#displaying-logged-in-user-on-dashboard-2.0"></a> Displaying logged in user on Dashboard 2.0</h2>
<p>Now you know how the user add on works, you are all set to display logged in users on Dashboard 2.0. To confirm this you can use a <code>debug</code> node that receives the <code>msg</code> object emitted by the Dashboard 2.0 widgets.</p>
<p>To display user information on the dashboard we will use Vue’s <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#teleports">Teleport</a> feature to render content to a specific location in the DOM, we will display user information at the action bar’s right-hand side.</p>
<ol>
<li>Drag a <code>ui-template</code> widget onto the canvas.</li>
<li>Click on that node, and select type as “Widget (Ui-Scoped)”. ( this allows us to render this ui-template at ui scoped which means I will not required to add separate ui-templates for different pages )</li>
<li>Copy the below vue snippet and paste that into the ui-template.</li>
</ol>
<div style="position: relative" id="code-container-132">
<pre class="language-html"><code id="code-132" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Teleporting user info to #app-bar-actions, which is the ID of the action bars' right corners area --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Teleport</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>loaded<span class="token punctuation">"</span></span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#app-bar-actions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>user-info<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Displaying user image --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">:src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>setup.socketio.auth.user.image<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br /> <span class="token comment"><!-- Greeting the user --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Hi, {{ setup.socketio.auth.user.name }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Teleport</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Flag to indicate if the component is loaded</span><br /> <span class="token literal-property property">loaded</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// This function is called when the component is inserted into the DOM.</span><br /> <span class="token comment">// Setting loaded to true here ensures the component is ready to access #app-bar-actions,</span><br /> <span class="token comment">// as it's now part of the same DOM structure.</span><br /> <span class="token comment">// Accessing it before mounted() would cause an error because the component wouldn't be initialized in the DOM yet.</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>loaded <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment">// Setting loaded to true to indicate that the component has been mounted successfully</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /><span class="token comment">/* Styling for user info display */</span><br /><span class="token selector">.user-info</span> <span class="token punctuation">{</span><br /> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br /> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br /> <span class="token property">gap</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /><span class="token comment">/* Styling for user avatar image*/</span><br /><span class="token selector">.user-info img</span> <span class="token punctuation">{</span><br /> <span class="token property">width</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span><br /> <span class="token property">height</span><span class="token punctuation">:</span> 24px<span class="token punctuation">;</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-132" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#deploying-the-flow"></a> Deploying the flow</h2>
<ol>
<li>With your flow updated to include the above, click the "Deploy" button in the top-right of the Node-RED Editor.</li>
<li>Navigate to <code>https://<your-instance-name>.flowfuse.cloud/dashboard</code>.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/displaying-logged-in-user-dashboard-view-DUWRO5S115-1817.avif 1817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/displaying-logged-in-user-dashboard-view-DUWRO5S115-1817.webp 1817w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of Dashboard displaying logged in user information" alt=""Screenshot of Dashboard displaying logged in user information"" loading="lazy" decoding="async" src="https://flowfuse.com/img/displaying-logged-in-user-dashboard-view-DUWRO5S115-1817.jpeg" width="1817" height="890" /></picture></p>
<h2 id="next-step" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#next-step"></a> Next step</h2>
<p>If you want to learn more about the FlowFuse Multiuser addon and Personalize Multiuser Dashboard. we do have many other resources, please refer to them to learn more.</p>
<ul>
<li><a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">Webinar</a> - This webinar provides an in-depth discussion of the Personalised Multi-User Dashboards feature and offers guidance on how to get started with it.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/">Personalised Multi-user Dashboards with Node-RED Dashboard 2.0</a> - This article explores the process of building multi-user Dashboards secured with FlowFuse Cloud.</li>
<li><a href="https://flowfuse.com/blueprints/flowfuse-dashboard/multi-user-dashboard/">Multi-User Dashboard for Ticket/Task Management</a> blueprint, which allows you to quickly utilize templates to develope Personalize multi-user dashboard.</li>
</ul>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/displaying-logged-in-users-on-dashboard/#conclusion"></a> Conclusion</h2>
<p>In this guide, we have demonstrated how to secure your dashboard and how to retrieve and display logged in user data on Dashboard 2.0. Additionally, we have discussed the functionality of the FlowFuse multi-user addon.</p>
<div>
<p>Looking to build a multi-user dashboard, deploy it in seconds, scale and manage Node-RED efficiently, and enable seamless remote access for your entire team?</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Displaying%20logged%20in%20user%20on%20Node-RED%20Dashboard%202.0">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/Node-RED Manufacturing ArchitectureEnhancing Factory Operations with Node-RED and FlowFuse2024-04-02T00:00:00Z<p>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 <a href="https://flowfuse.com/solutions/uns/">Unified Namespace</a>, a concept I explore in depth in this article <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-unified-namespace-architecture/">"Node-RED in a Unified Namespace Architecture."</a> 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.</p>
<!--more-->
<h2 id="understanding-the-multilayered-approach%3A-the-foundation-of-factory-architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/#understanding-the-multilayered-approach%3A-the-foundation-of-factory-architecture"></a> Understanding the Multilayered Approach: The Foundation of Factory Architecture</h2>
<p>For illustrative purposes, I’ve opted to reference the <a href="https://webstore.ansi.org/preview-pages/ISA/preview_S_990001_2007.pdf">Purdue / ISA-99 Model</a>, 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.</p>
<p><strong>ISA-99 Visualization</strong></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/purdue-model-Y6zfzd1u0u-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/purdue-model-Y6zfzd1u0u-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Purdue Model" loading="lazy" decoding="async" src="https://flowfuse.com/img/purdue-model-Y6zfzd1u0u-1920.jpeg" width="1920" height="1326" /></picture></p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-architecture-aiWrW6oCVi-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-architecture-aiWrW6oCVi-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Node-RED Manufacturing Architecture" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-architecture-aiWrW6oCVi-1600.jpeg" width="1600" height="900" /></picture></p>
<h2 id="applications-across-factory-layers" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/#applications-across-factory-layers"></a> Applications Across Factory Layers</h2>
<p><strong>Shopfloor:</strong> 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 <a href="https://dashboard.flowfuse.com/">Dashboard module</a>, 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.</p>
<p><strong>Edge:</strong> 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.</p>
<p><strong>Enterprise Network:</strong> 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.</p>
<h3 id="the-device-agent%3A-empowering-remote-node-red-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/#the-device-agent%3A-empowering-remote-node-red-instances"></a> The Device Agent: Empowering Remote Node-RED Instances</h3>
<p>Every Node-RED instance that isn’t hosted within the Enterprise/Cloud 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.</p>
<h3 id="how-to-communicate-between-node-red-instance%3F-project-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/#how-to-communicate-between-node-red-instance%3F-project-nodes"></a> How to communicate between Node-RED Instance? Project Nodes</h3>
<p>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.</p>
<p>These <a href="https://flowfuse.com/docs/user/projectnodes/">Project Links</a> 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.</p>
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/node-red-architecture/#summary"></a> Summary</h2>
<p>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.</p>
<div>
<p>Optimize your factory operations with FlowFuse. Effortlessly manage, deploy, scale, and secure your Node-RED instances.</p>
<!-- Defaulting to '#' if cta_url is not defined -->
<a href="https://app.flowfuse.com/account/create?utm_campaign=60718323-BCTA&utm_source=blog&utm_medium=cta&utm_term=high_intent&utm_content=Node-RED%20Manufacturing%20Architecture">
Sign Up for FlowFuse Now to Get Started
</a>
</div>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/Using Kafka with Node-REDStep-by-step Guide to Using Kafka with Node-RED for sending and receiving data.2024-03-29T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/">how Kafka is applied in manufacturing</a>, where we've covered the basics and practical applications extensively.</p>
<h2 id="discussing-problem-and-potential-solution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#discussing-problem-and-potential-solution"></a> Discussing problem and potential solution</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<h2 id="installing-and-running-kafka-locally" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#installing-and-running-kafka-locally"></a> Installing and running Kafka locally</h2>
<p>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.</p>
<ol>
<li>Pull the zookeeper image if it is not already, and run the zookeeper container.</li>
</ol>
<pre><code>docker run -p 2181:2181 zookeeper
</code></pre>
<ol start="2">
<li>Pull the Kafka image if it is not already, and run Kafka Container, expose PORT 9092.</li>
</ol>
<pre><code>docker run -p 9092:9092 `
-e KAFKA_ZOOKEEPER_CONNECT=<Your_Private_Ip>.1:2181 `
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://<Your_Private_Ip>:9092 `
-e KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 `
confluentinc/cp-kafka
</code></pre>
<h2 id="running-kafka-on-the-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#running-kafka-on-the-cloud"></a> Running Kafka on the cloud</h2>
<p>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.</p>
<p>During the writing of this tutorial, I utilized <a href="https://aiven.io/kafka-connect">Aiven’s cloud data platform</a> 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.</p>
<h2 id="installing-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#installing-dashboard-2.0"></a> Installing Dashboard 2.0</h2>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting started with Dashboard 2.0</a>, which covers everything from basic concepts to installation and creating your first dashboard seamlessly.</p>
<h2 id="installing-and-configuring-kafka-custom-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#installing-and-configuring-kafka-custom-node"></a> Installing and configuring Kafka custom node</h2>
<ol>
<li>Install <code>node-red-kafka-manager</code> by the palette manager.</li>
<li>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.</li>
</ol>
<ul>
<li>Host: The IP address or hostname of your Kafka broker server.</li>
<li>Port: Kafka typically uses port 9092 by default. Ensure this aligns with your Kafka broker's configuration.</li>
<li>SSL Configuration (if applicable):
CA Certificate: The Certificate Authority (CA) certificate for validating the SSL connection.</li>
<li>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.'</li>
<li>Username: SASL username of Kafka broker server for authentication.</li>
<li>Password: SASL password of Kafka broker server for authentication.</li>
</ul>
<ol start="3">
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-configuration-pb02_fgC5E-644.avif 644w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-configuration-pb02_fgC5E-644.webp 644w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing configuration of kafka" alt=""Screenshot showing configuration of kafka"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-kafka-configuration-pb02_fgC5E-644.jpeg" width="644" height="797" /></picture></p>
<ol start="4">
<li>Enable the TLS option if your Kafka broker server is using it for secure communication.</li>
<li>After enabling the TLS option click on the edit icon next to <code>add new tls-config</code> and upload the CA Certificate in PEM format.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-tls-configuration-4pBKpIBwjr-566.avif 566w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-tls-configuration-4pBKpIBwjr-566.webp 566w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Configuring tls for kafka" alt=""Screenshot showing TLS configuration for kafka"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-tls-configuration-4pBKpIBwjr-566.jpeg" width="566" height="822" /></picture></p>
<h2 id="adding-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#adding-environment-variables"></a> Adding Environment variables</h2>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment Variables in Node-RED</a> for more information.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-650.avif 650w, https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-650.webp 650w, https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-650.jpeg 650w, https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing adding environment variable in Node-RED" alt=""Screenshot showing adding environment variable in Node-RED"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-setting-environment-variables-Tg83SAYzFJ-650.jpeg" width="1300" height="580" /></picture></p>
<ol>
<li>Navigate to the instance's setting and then go to the environment section.</li>
<li>Click on the <code>add variable</code> 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:</li>
</ol>
<pre><code>[{"host":<enter IP address or hostname of your Kafka broker server >,"port":<enter port on which your kafka broker server is listening>}]
</code></pre>
<ol start="3">
<li>Click on the save button and restart the instance by clicking on the top right Action button and selecting the restart option.</li>
</ol>
<h2 id="creating-a-new-kafka-topic" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#creating-a-new-kafka-topic"></a> Creating a new Kafka topic</h2>
<p>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.</p>
<ol>
<li>Drag an inject node onto Canvas.</li>
<li>Set <code>msg.topic</code> to <code>createTopics</code> string.</li>
<li>Set <code>msg.payload</code> to <code>[{"topic": "Temperature","partitions": 3,"replicationFactor":1}]</code>, you can create as many topics as you want at a time.</li>
<li>Drag the Kafka admin node onto Canvas.</li>
<li>Connect the inject node’s output to the Kafka admin node’s input.</li>
<li>Deploy the flow by clicking on the top-right red deploy button.</li>
<li>After the Deploy, click on the inject button to create a topic.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-inject-node-l979AwGeBe-647.avif 647w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-inject-node-l979AwGeBe-647.webp 647w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing set payload in inject node for creating new Kafka topic" alt="Screenshot showing set payload in inject node for creating new Kafka topic"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-inject-node-l979AwGeBe-647.jpeg" width="647" height="583" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-creating-topic-JruaRjfqlc-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-creating-topic-JruaRjfqlc-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing Node-RED flow for creating new kafka topic" alt=""Screenshot showing Node-RED flow for creating new kafka topic"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-creating-topic-JruaRjfqlc-650.jpeg" width="650" height="138" /></picture></p>
<h2 id="sending-data-to-kafka-topic" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#sending-data-to-kafka-topic"></a> Sending Data to Kafka topic</h2>
<p>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.</p>
<p>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.</p>
<ol>
<li>Drag a Kafka producer node onto Canvas.</li>
<li>Selected added Kafka configuration.</li>
<li>Click on that node and add the topic that we have created, set the key as <code>downtown</code>, and set the partition number as 0. (The partition index starts from zero)</li>
<li>Drag an inject node onto Canvas.</li>
<li>Set <code>msg.payload</code> to <code>$floor($random() * 100)</code> as a JSON expression and set the inject node to send the payload automatically after a specific interval of time.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-producer-i1kIRh8Uqz-505.avif 505w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-producer-i1kIRh8Uqz-505.webp 505w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing kafka producer configuration" alt=""Screenshot showing kafka producer configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-kafka-producer-i1kIRh8Uqz-505.jpeg" width="505" height="823" /></picture></p>
<h2 id="receiving-data-from-kafka-topic" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#receiving-data-from-kafka-topic"></a> Receiving data from Kafka topic</h2>
<p>In this section, we will be creating consumers who will subscribe to listen to downtown region temperature data.</p>
<ol>
<li>Drag the Kafka consumer node onto Canvas.</li>
<li>Selected added Kafka configuration.</li>
<li>Click on that node, add the topic that we have created, and enter partition <code>0</code> from which it will read temperature data.</li>
<li>Add the Change node onto the Canvas and set <code>msg.topic</code> to <code>msg._kafka.key</code> and <code>msg.payload</code> to <code>$number(msg.payload)</code> 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)</li>
<li>Add the ui-chart node on to Canvas and select the created group in which the chart will render.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-consumer-zHyznIGKTA-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-kafka-consumer-zHyznIGKTA-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing kafka consumer configuration" alt=""Screenshot showing kafka consumer configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-kafka-consumer-zHyznIGKTA-650.jpeg" width="650" height="621" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-change-node-WmW0KqdNa8-542.avif 542w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-change-node-WmW0KqdNa8-542.webp 542w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Changing payload received from Kafka consumer" alt=""Changing payload received from Kafka consumer"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-change-node-WmW0KqdNa8-542.jpeg" width="542" height="581" /></picture></p>
<p>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.</p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#deploying-the-flow"></a> Deploying the flow</h2>
<p>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 <code>https://<your-instance-name>.flowfuse.cloud/dashboard</code></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-650.avif 650w, https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-650.webp 650w, https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-650.jpeg 650w, https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing Node-RED flow of Real-time temperature monitoring system" alt=""Screenshot showing Node-RED flow of Real-time temperature monitoring system"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-flow-kgq7yJxt-a-650.jpeg" width="1300" height="628" /></picture></p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-EOBsi1SDwN-600.gif 600w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing Dashboard 2.0 view of Real-time temperature monitoring system" alt=""Screenshot showing Dashboard 2.0 view of Real-time temperature monitoring system"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-with-node-red-temperature-monitoring-system-EOBsi1SDwN-600.webp" width="600" height="338" /></picture></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/flowfuse-gallarus-strategic-partnership-to-accelerate-industry-4-adoption/FlowFuse and Gallarus Announce Strategic Partnership to Accelerate Industry 4.0 AdoptionStrategic partnership to empower businesses with low-code development for Industry 4.0 Transformation2024-03-28T00:00:00Z<p>FlowFuse, a leading provider of the low-code end-to-end development platform for industrial applications, and Gallarus Industry Solutions Limited, the leading industry 4.0 integrator in Europe dedicated to digital transformation through the deployment of the Unified Namespace (UNS) digital architecture, today announced an exciting strategic partnership. This collaboration aims to empower businesses with advanced solutions for Industry 4.0 transformation and optimized operational efficiency.</p>
<!--more-->
<p>Powered by Node-RED, FlowFuse enables teams of all sizes to harness low-code capabilities to build robust applications with unparalleled scalability, and security. FlowFuse’s collaborative environment and scalable architecture ensure that teams can build securely and together for continuous development and operational success. Gallarus, with its extensive experience in digital transformation projects and industrial technology integration, will leverage its expertise in high-quality project implementation, support, and maintenance. Together, they offer a comprehensive solution for businesses seeking to digitally transform their operations.</p>
<p>“We are pleased to announce this strategic alliance with Gallarus,” said Zeger-Jan van de Weg, CEO at FlowFuse. “This partnership signifies a significant step forward in providing businesses with the tools they need to excel in the current digital landscape. By combining our expertise, we are confident in delivering exceptional value to our customers.”
“This partnership with FlowFuse perfectly aligns with our mission to empower businesses with transformative industry 4.0 solutions,” said Patrick Mc Carthy, COO at Gallarus Industry Solutions Limited. “FlowFuse's low-code development platform, combined with our expertise in UNS architecture and integration, will provide a powerful solution for organizations of all sizes to move away from an Industry 3.0 mentality and embrace Industry 4.0, streamlining operations to unlock new levels of efficiency not seen before.”</p>
<p>This combined expertise will address the complexities of digital transformation for businesses by:</p>
<ul>
<li>Facilitating data access, transformation, and visualization across any protocol.</li>
<li>Breakdown data silos enabling organization-wide data availability via the UNS.</li>
<li>Enable citizen developers to build extremely useful industrial applications.</li>
</ul>
<p>For more information about FlowFuse and Gallarus Industry Solutions Limited, please visit their respective websites at <a href="http://flowfuse.com/">flowfuse.com</a> and <a href="http://gis.ie/">gis.ie.</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/Getting Started with Node-RED Dashboard 2.0New to Node-RED? New to Dashboard 2.0? This guide will help you get started.2024-03-27T00:00:00ZJoe Pavitt<p>With our latest release of Node-RED Dashboard 2.0, we've made some big improvements to the onboarding experience.</p>
<p>We're seeing over 2,000 people download Dashboard 2.0 per week, and are seeing a great buzz in the community of brand new Node-RED users, experienced Node-RED users that haven't explored a UI solution previously and existing users migrating from Dashboard 1.0.</p>
<p>So, with that in mind, we wanted to offer a new "Getting Started" guide that will help you get up and running with building custom user interfaces and data visualizations in Node-RED.</p>
<!--more-->
<h2 id="how-to-install-node-red-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#how-to-install-node-red-dashboard-2.0"></a> How to Install Node-RED Dashboard 2.0</h2>
<h3 id="step-1%3A-%22manage-palette%22" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#step-1%3A-%22manage-palette%22"></a> Step 1: "Manage Palette"</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-manage-palette-xMbApteq_f-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-manage-palette-xMbApteq_f-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot to show where to find the "Manage Palette" option in Node-RED" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-manage-palette-xMbApteq_f-1920.jpeg" width="1920" height="1102" /></picture></p>
<figcaption>Screenshot to show where to find the "Manage Palette" option in Node-RED</figcaption>
<ol>
<li>Click the Node-RED Settings (top-right)</li>
<li>Click "Manage Palette"</li>
</ol>
<h3 id="step-2%3A-search-%26-%22install%22" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#step-2%3A-search-%26-%22install%22"></a> Step 2: Search & "Install"</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-search-install-nhnZ52tZ_L-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-search-install-nhnZ52tZ_L-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot to show where to find the "Install" tab, and how to find @flowfuse/node-red-dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-search-install-nhnZ52tZ_L-1920.jpeg" width="1920" height="1102" /></picture></p>
<figcaption>Screenshot to show where to find the "Install" tab, and how to find @flowfuse/node-red-dashboard</figcaption>
<ol>
<li>Switch to the "Install" tab</li>
<li>Search for <em>"@flowfuse/node-red-dashboard"</em></li>
<li>Click "Install"</li>
</ol>
<h2 id="adding-your-first-widgets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#adding-your-first-widgets"></a> Adding your first widgets</h2>
<p>With the nodes installed, getting started is as easy as choosing a node from the Palette (the left-hand side list of nodes) in Node-RED, and dropping it onto your canvas.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/dashboard-getting-started-add-widget-95NyxoNYz7-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screen recording to show how easy it is to deploy your first Dashboard 2.0 application." loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-add-widget-95NyxoNYz7-800.webp" width="800" height="460" /></picture></p>
<figcaption>Screen recording to show how easy it is to deploy your first Dashboard 2.0 application.</figcaption>
<p>In this case, we drop in a <code>ui-button</code>, click "Deploy" and then can see the button running live in our user interface.</p>
<p>Notice too that Dashboard will automatically setup some underlying configurations for you (visible in the right-side menu):</p>
<ul>
<li><code>ui-base</code>: Each instance of Node-RED that uses Dashboard 2.0 must have a single <code>ui-base</code> element (we're hoping to add support for multiple in the future). This element contains all of the global settings for your Dashboard instance.</li>
<li><code>ui-page</code>: A single Dashboard (<code>ui-base</code>) can consist of multiple pages, and can be navigated to using the left-side sidebar. Each page is then responsible for displaying a collection of <code>ui-group</code> elements.</li>
<li><code>ui-group</code>: Each group contains a collection of widgets, and can be used to organize your Dashboard into logical sections.</li>
<li><code>ui-theme</code>: Each <code>ui-page</code> can be assigned a given theme. Your "Themes" provide control over the aesthetic of your Dashboard like color, padding and margins.</li>
</ul>
<h2 id="configuring-your-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#configuring-your-layout"></a> Configuring your layout</h2>
<p>Dashboard 2.0 adds a dedicated sidebar to Node-RED to provide a centralized view of your pages, groups and widgets. From here you can add new pages and groups, modify existing settings, and re-order content to your liking.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-sidebar-s6ArRRciML-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-sidebar-s6ArRRciML-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot showing the Dashboard 2.0 sidebar in the Node-RED Editor." loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-sidebar-s6ArRRciML-1920.jpeg" width="1920" height="1102" /></picture></p>
<figcaption>Screenshot showing the Dashboard 2.0 sidebar in the Node-RED Editor.</figcaption>
<p>When defining your layout options, we break the choice into two sections:</p>
<ul>
<li><strong>Page Layout:</strong> Controls how the <code>ui-groups</code>'s are presented on a given page in your application.</li>
<li><strong>Navigation Sidebar:</strong> Defines the left-side navigation style, defined at the <code>ui-base</code> level.</li>
</ul>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-layout-pROZ0TkQTa-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-layout-pROZ0TkQTa-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example of a "Grid" page layout, with "Collapsing" sidebar navigation." loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-layout-pROZ0TkQTa-1920.jpeg" width="1920" height="1110" /></picture></p>
<figcaption>Example of the "Grid" page layout, with "Collapsing" sidebar navigation.</figcaption>
<h3 id="page-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#page-layout"></a> Page Layout</h3>
<p>Currently, we have three different options for page layout:</p>
<ul>
<li><strong>Grid:</strong> (<a href="https://dashboard.flowfuse.com/layouts/types/grid.html">docs</a>) This is the default layout for a page, and uses a 12-column grid system to layout your <code>ui-groups</code>. Widths of groups and widgets define the number of columns they will render in. So, a "width" of 6" would render to 50% of the screen. Grid layouts are entirely responsive, and will adjust to the size of the screen.</li>
<li><strong>Fixed:</strong> (<a href="https://dashboard.flowfuse.com/layouts/types/fixed.html">docs</a>) Each component will render at a <em>fixed</em> width, no matter what the screen size is. The "width" property is converted a fixed pixel value (multiples of 48px by default).</li>
<li><strong>Notebook:</strong> (<a href="https://dashboard.flowfuse.com/layouts/types/notebook.html">docs</a>) This layout will stretch to 100% width, up to a maximum width of 1024px, and will centrally align. It's particularly useful for storytelling (e.g. articles/blogs) or analysis type user interfaces (e.g. Jupyter Notebooks), where you want the user to digest content in a particular order through scrolling.</li>
</ul>
<h3 id="navigation-sidebar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#navigation-sidebar"></a> Navigation Sidebar</h3>
<p>Dashboard 2.0 offers various options on the appearance of the navigation sidebar:</p>
<ul>
<li><strong>Collapsing:</strong> When the sidebar is opened the page content will adjust with the width of the sidebar.</li>
<li><strong>Fixed:</strong> The full sidebar will always be visible, and the page content will adjust to the width of the sidebar.</li>
<li><strong>Collapse to Icons:</strong> When minimized, users can still navigate between pages by clicking on the icons representing each page in the sidebar.</li>
<li><strong>Appear over Content:</strong> When the sidebar is opened, the page is given an overlay, and the sidebar sits on top.</li>
<li><strong>Always Hide:</strong> The sidebar will never show, and navigation between pages can instead be driven by <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-control.html"><code>ui-control</code></a>.</li>
</ul>
<h3 id="define-your-layout" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#define-your-layout"></a> Define Your Layout</h3>
<p>In our example, we're going to switch to a "Notebook" layout, with a "Collapse to Icons" sidebar:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-example-4OZNSC6E9z-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-example-4OZNSC6E9z-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Example of the "Notebook" layout and "Collapse to icons" sidebar" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-example-4OZNSC6E9z-1920.jpeg" width="1920" height="1112" /></picture></p>
<figcaption>Example of the "Notebook" layout, with "Collapse to Icons" sidebar navigation.</figcaption>
<h2 id="adding-more-widgets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#adding-more-widgets"></a> Adding More Widgets</h2>
<p>Now, we're going to build a quick example to demonstrate how we can wire nodes together, and visualize the output from a <code>ui-slider</code> onto a <code>ui-chart</code>.</p>
<h3 id="adding-a-group" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#adding-a-group"></a> Adding a Group</h3>
<p>In the Node-RED Editor's Dashboard 2.0 sidebar, we're going to then do the following things:</p>
<ol>
<li>Edit "My Group" and rename it to "Controls"</li>
<li>Create a new "Group" in your existing page called "Data Visualization"</li>
</ol>
<p>You'll now see the two groups listed under "Page 1". "Controls" with a single <code>ui-button</code> and "Data Visualization" with no widgets.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-new-group-AHNnsSIu4I-1586.avif 1586w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-new-group-AHNnsSIu4I-1586.webp 1586w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the modified and newly added groups" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-new-group-AHNnsSIu4I-1586.jpeg" width="1586" height="972" /></picture></p>
<figcaption>Screenshot of the modified and newly added groups</figcaption>
<h3 id="connecting-new-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#connecting-new-nodes"></a> Connecting New Nodes</h3>
<p>Then, we're going to add two new widgets:</p>
<ul>
<li>UI Chart</li>
<li>UI Slider</li>
</ul>
<p>Which we can do by dropping them from the left-side Palette and onto our canvas.</p>
<p>We'll need to double-click each new node and confirm which "Group" we want to add this node to. In this case, we'll add the <code>ui-slider</code> to the "Controls" group, and the <code>ui-chart</code> to the "Data Visualization" group.</p>
<p>We're also going to connect the output from both the <code>ui-slider</code> and <code>ui-button</code> to the input of the <code>ui-chart</code>:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-flow-lz1mTarEt6-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-flow-lz1mTarEt6-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Node-RED Editor, showing the ui-slider and ui-button connected to our ui-chart" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-flow-lz1mTarEt6-1920.jpeg" width="1920" height="1101" /></picture></p>
<figcaption>Screenshot of the Node-RED Editor, showing the ui-slider and ui-button connected to our ui-chart</figcaption>
<p>Now, when we view our Dashboard, we can see the <code>ui-slider</code> output is def straight into our <code>ui-chart</code>:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-final-QAE6kfCc3P-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-final-QAE6kfCc3P-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Screenshot of the Dashboard with all three widgets rendered" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-final-QAE6kfCc3P-1920.jpeg" width="1920" height="1112" /></picture></p>
<figcaption>Screenshot of the Dashboard with all three widgets rendered</figcaption>
<p>The final step we're going to make is to modify our <code>ui-button</code>. We're going to rename it to "Clear", and configure it's "Payload" option to send a JSON payload of <code>[]</code>, which, when sent to the <code>ui-chart</code> will clear the chart of all data.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-getting-started-btn-config-udwduPdQw9-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-getting-started-btn-config-udwduPdQw9-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="The ui-button configuration after setting it's payload and label" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-btn-config-udwduPdQw9-1920.jpeg" width="1920" height="950" /></picture></p>
<figcaption>The ui-button configuration after setting it's payload and label</figcaption>
<p>With all of this together, we have the following functional Dashboard:</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/dashboard-getting-started-final-ycsdL19nfh-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="Short animation showing the final functional dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-getting-started-final-ycsdL19nfh-800.webp" width="800" height="450" /></picture></p>
<figcaption>Short animation showing the final functional dashboard.</figcaption>
<h2 id="next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#next-steps"></a> Next Steps</h2>
<p>Whilst this is just a simple introduction of Node-RED Dashboard 2.0, we do have many other articles and documentation that can help you get started with more advanced features.</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/dashboard/">FlowFuse Dashboard Articles</a> - Collection of examples and guides written by FlowFuse.</li>
<li><a href="https://dashboard.flowfuse.com/">Node-RED Dashboard 2.0 Documentation</a> - Detailed information for each of the nodes available in Dashboard 2.0, as well as useful guides on building custom nodes and widgets of your own.</li>
<li><a href="https://discourse.nodered.org/tag/dashboard-2">Node-RED Forums - Dashboard 2.0</a> - The Node-RED forums is a great place to ask questions, share your projects and get help from the community.</li>
<li><a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">Beginner Guide to a Professional Node-RED</a> - A free guide to an enterprise-ready Node-RED. Learn all about Node-RED history, securing your flows and dashboard data visualization.</li>
<li><a href="https://flowfuse.com/contact-us/">FlowFuse - Book a Demo</a> - FlowFuse provides a complete platform to scale your production Node-RED applications, increase developer velocity, and enhance security in order to accelerate innovation.</li>
</ul>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/#follow-our-progress"></a> Follow our Progress</h2>
<p>New features and improvements are coming to Node-RED Dashboard 2.0 every week, if you're interested in what we have lined up, or want to contribute yourself, then you can track the work we have lined up on our GitHub Projects:</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/5">Dashboard 1.0 Feature Parity Tracker</a></li>
</ul>
<p>If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/Securing HTTP Traffic for Node-RED with FlowFuseChoosing the right form of authentication for your Node-RED integration is important.2024-03-26T00:00:00Z<p>Citizen development empowers employees to create digital solutions. However, it requires guardrails to ensure data security, operational stability, and compliance. These guardrails are what FlowFuse provides to the Node-RED community to level up their deployments. FlowFuse offers many different security measures for authentication and authorization, which all apply to different scenarios.</p>
<p>In this post we’ll take a look at most of them, specifically for HTTP traffic. We’ll discuss the trade-offs for auditabliltiy, convenience to use as either machine or human, among other factors.</p>
<!--more-->
<h2 id="http-basic-authentication%3A-a-simple-approach" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/#http-basic-authentication%3A-a-simple-approach"></a> HTTP Basic Authentication: A Simple Approach</h2>
<p>HTTP Basic Authentication is widely supported and straightforward to implement, making it a popular choice for securing APIs. It requires users to provide a username and password before accessing the Node-RED instance. While this method is easy to use, it's important to note that the username and password are shared and transmitted in plain text, making it vulnerable to interception if the connection doesn't leverage SSL/TLS. FlowFuse by default ensures SSL/TLS is deployed.</p>
<h2 id="personal-access-tokens%3A-knowing-who-accessed-the-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/#personal-access-tokens%3A-knowing-who-accessed-the-node-red"></a> Personal access tokens: Knowing who accessed the Node-RED</h2>
<p>Personal access tokens (PATs) are an essential component of FlowFuse, allowing users to securely access their accounts without sharing their passwords. These tokens are generated by the user and can be used to authenticate to the SaaS product's API or other services. PATs provide a more secure alternative to traditional username/password authentication, as they can be revoked or regenerated at any time, limiting the potential impact of a compromised token.</p>
<h2 id="flowfuse-authentication%3A-seamless-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/#flowfuse-authentication%3A-seamless-integration"></a> FlowFuse Authentication: Seamless Integration</h2>
<p>FlowFuse authentication offers a seamless and secure way for users to access dashboards and other resources that are typically accessed through a browser. It leverages single sign-on (SSO) and SAML 2.0, reducing the management burden for organizations.</p>
<p>For users this is convenient as they can access multiple applications and resources using a single set of credentials, eliminating the need to remember and manage multiple passwords.</p>
<p>For organizations, SSO enhances security by centralizing authentication and authorization, reducing the risk of unauthorized access. By leveraging SSO and SAML 2.0, FlowFuse takes care of user management, freeing up customers from the administrative burden of managing user accounts and passwords. FlowFuse authentication adheres to industry-standard security protocols, ensuring compliance with regulatory requirements.</p>
<p>This method of authentication is however impractical for API access by other services, to programmatically transfer data between them.</p>
<h2 id="bearer-authentication%3A-a-token-based-approach" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/#bearer-authentication%3A-a-token-based-approach"></a> Bearer Authentication: A Token-Based Approach</h2>
<p>Bearer Authentication offers a more secure and flexible alternative to traditional username/password authentication. Users will generate the token through the FlowFuse platform with each instance generating its own token. These tokens can be designed to have limited lifespans, reducing the risk if compromised. In the case the token then becomes compromised only the instance in which the token is generated can become subject to malicious behaviors. In the case that this does occur, simply deleting the token will elevate any unwanted access.</p>
<p>Compared to FlowFuse Authentication, this method is very well suited for API access and programmatic access to FlowFuse.</p>
<h2 id="choosing-the-right-authentication-strategy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/#choosing-the-right-authentication-strategy"></a> Choosing the Right Authentication Strategy</h2>
<p>FlowFuse provides multiple authentication mechanisms to cater to various aspects of security and user experience. When designing your Node-RED applications, consider the specific requirements of your project and the patterns of user interaction to select the most appropriate authentication strategy. By doing so, you can ensure both a high level of security and an optimal user experience for your web development projects with API calls through Node-RED.</p>
<p>In conclusion, understanding and utilizing these different types of authentication in FlowFuse empowers citizen developers like you to create more secure and efficient applications for diverse use cases.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/Scaling Node-RED with FlowFuse: Differences between a FlowFuse Instance and a Device InstanceManaging your Node-RED instances is easier with FlowFuse.2024-03-25T00:00:00Z<p>FlowFuse is a Software as a Service (SaaS) platform designed to enhance the experience and capabilities of Node-RED for its users. By focusing on scalability, security, and Dev Ops, FlowFuse aims to remove some of the technical barriers associated with using Node-RED, making it easier for citizen developers to automate tasks, process data, and create applications. In this blog post, we will discuss the differences between a FlowFuse instance and a FlowFuse device instance while highlighting how FlowFuse addresses scalability challenges in Node-RED deployments.</p>
<!--more-->
<h2 id="scalability-challenges-with-traditional-node-red-deployments" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/#scalability-challenges-with-traditional-node-red-deployments"></a> Scalability Challenges with Traditional Node-RED Deployments</h2>
<p>While deploying Node-RED is quite simple, managing multiple instances across different environments can become complex and time-consuming. As the number of devices and use cases grow, users face difficulties in scaling their Node-RED applications efficiently to handle increased load without compromising performance or security. This is where FlowFuse comes into play.</p>
<h2 id="the-role-of-flowfuse-as-an-orchestration-tool" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/#the-role-of-flowfuse-as-an-orchestration-tool"></a> The Role of FlowFuse as an Orchestration Tool</h2>
<p>FlowFuse functions as an orchestration tool that allows the deployment and management of all your Node-RED instances at scale, addressing scalability challenges head-on. By leveraging its platform, users can quickly deploy and manage multiple Node-RED instances while ensuring optimal performance and security. This enables them to connect with a wide range of devices, from PLCs and sensors to legacy software, without worrying about the complexities of managing their Node-RED deployment.</p>
<h2 id="deploying-node-red-next-to-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/#deploying-node-red-next-to-devices"></a> Deploying Node-RED Next to Devices</h2>
<p>One common issue in IoT deployments is that device instances of Node-RED often communicate with unsecure devices or networks. To mitigate security risks and ensure data protection, it's common to deploy Node-RED in close proximity to these devices. The FlowFuse platform uses <a href="https://flowfuse.com/product/device-agent/">device agents</a> that communicate back to the platform via a reverse tunnel over port 443. This setup requires only one firewall rule: allowing outbound connections from the <a href="https://flowfuse.com/product/device-agent/">device agent</a> running Node-RED to the FlowFuse platform, significantly minimizing security risks while enabling remote monitoring, flow editing, and configuration deployment at scale.</p>
<h2 id="deploying-node-red-instances-within-the-flowfuse-platform" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/#deploying-node-red-instances-within-the-flowfuse-platform"></a> Deploying Node-RED Instances Within the FlowFuse Platform</h2>
<p>Not all instances of Node-RED need to be deployed at the edge and can be deployed anywhere. FlowFuse offers this flexibility in cases where users prefer or require deploying their Node-RED instances within the platform itself. This capability allows users to focus on developing and managing their applications without worrying about the underlying infrastructure.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/scaling-node-red-devices-vs-flowfuse-instance/#conclusion"></a> Conclusion</h2>
<p>FlowFuse addresses scalability challenges in Node-RED deployments by providing an easy-to-use platform that enables users to manage multiple instances at scale while maintaining security and performance. By understanding the differences between a FlowFuse instance and a device instance, you can make informed decisions about your deployment strategy and leverage the full potential of Node-RED for your applications. Stay tuned for our upcoming blogs where we will dive deeper into the areas of security, dev ops, and backup solutions provided by FlowFuse.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/How Kafka is applied in manufacturingAn overview of Kafka -- How it's applied for industrial applications, and how it works2024-03-15T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<p>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.</p>
<h2 id="what-is-kafka%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#what-is-kafka%3F"></a> What is Kafka?</h2>
<p>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.</p>
<p>Kafka is also a very good <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/">broker for UNS architecture</a>.</p>
<h2 id="understanding-kafka's-architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#understanding-kafka's-architecture"></a> Understanding Kafka's Architecture</h2>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-kafka-in-manufacturing-kafka-architecture-c7g889LaEF-1600.avif 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-kafka-in-manufacturing-kafka-architecture-c7g889LaEF-1600.webp 1600w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Architecture of Kafka" alt=""Architecture of Kafka"" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-kafka-in-manufacturing-kafka-architecture-c7g889LaEF-1600.jpeg" width="1600" height="1519" /></picture></p>
<p><strong>1. Topics and Partitions</strong></p>
<ul>
<li>Topics: Imagine topics as folders for organizing data – they act as distinct categories. Kafka arranges information into these topics for systematic storage.</li>
<li>Partitions: Think of partitions as subdivisions within topics. They enable parallel processing across multiple servers, enhancing fault tolerance and throughput.</li>
</ul>
<p><strong>2. Producers:</strong></p>
<ul>
<li>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.</li>
</ul>
<p><strong>3. Brokers:</strong></p>
<ul>
<li>Definition: Brokers are the backbone servers in a Kafka cluster.</li>
<li>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.</li>
</ul>
<p><strong>4. Consumers and Consumer Groups:</strong></p>
<ul>
<li>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.</li>
<li>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.</li>
</ul>
<p><strong>5. Offsets</strong></p>
<ul>
<li>Definition: Offsets act as unique identifiers for records within a partition. They denote the position of a consumer in the partition.</li>
<li>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.</li>
</ul>
<p><strong>6. Replication</strong></p>
<ul>
<li>Mechanism: Kafka ensures data durability by replicating partitions across multiple brokers.</li>
<li>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.</li>
</ul>
<h2 id="features-of-kafka" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#features-of-kafka"></a> Features of Kafka</h2>
<p>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.</p>
<ul>
<li><strong>High Throughput and Scalability:</strong> Kafka can handle thousands of messages per second and can scale horizontally and vertically to meet growing data demands without compromising performance.</li>
<li><strong>Fault Tolerance and Reliability:</strong> 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.</li>
<li><strong>Real-Time Processing and Low Latency:</strong> Kafka's real-time processing ensures low latency for instant data analysis, which is critical for real-time decision-making.</li>
</ul>
<h2 id="applications-of-kafka" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#applications-of-kafka"></a> Applications of Kafka</h2>
<p>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.</p>
<p><strong>1. Manufacturing Operations Optimization:</strong></p>
<ul>
<li>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.</li>
<li>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.</li>
</ul>
<p><strong>2. Predictive Maintenance:</strong> 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</p>
<p><strong>3. Supply Chain Management:</strong> 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.</p>
<p><strong>4. Logistics and Transportation:</strong> Companies use Kafka to track vehicle and shipment locations in real-time, optimizing routes through the processing of streams of GPS data.</p>
<p><strong>5. Telecommunications:</strong> 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.</p>
<p><strong>6. Financial Services:</strong> 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.</p>
<h2 id="challenges-and-considerations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#challenges-and-considerations"></a> Challenges and Considerations</h2>
<p>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.</p>
<ol>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
</ol>
<p>Additionally, if you are interested in learning more about Kafka and its practical implementation, refer to our guide on Using <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/">Kafka with Node-RED</a>.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-in-manufacturing/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/flowfuse-self-hosted-starter-resource-limits/FlowFuse Open Source Starter Tier Resource LimitsThe first 5 Node-RED runtimes are part of the starter package going forward2024-03-14T00:00:00ZZJ van de Weg<p>Today, with the latest FlowFuse release, an important change was made that
updates the resource limits for our open-source, self-managed FlowFuse server
tier. This change does not affect FlowFuse Cloud users in any way.</p>
<!--more-->
<h2 id="new-starter-tier-resource-limits" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/flowfuse-self-hosted-starter-resource-limits/#new-starter-tier-resource-limits"></a> New Starter Tier Resource Limits</h2>
<p>The new limit for the number of Node-RED runtimes on the Starter tier is 5.
These 5 instances can be distributed across a maximum of 5 teams. This revised
structure allows for the development of your initial FlowFuse and Node-RED
solutions, providing a clear understanding of how FlowFuse can enhance your
organization's workflows. Upgrading to a Team or Enterprise tier license unlocks
the full potential of FlowFuse and grants access to our dedicated support team.</p>
<h2 id="why-the-change%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/flowfuse-self-hosted-starter-resource-limits/#why-the-change%3F"></a> Why the Change?</h2>
<p>Here at FlowFuse, we're committed to providing a great experience for all our
users, including those utilizing our free, open-source Starter tier. We
initially designed this tier to offer a platform for small organizations and
larger teams to explore FlowFuse's capabilities without needing our direct
involvement. However, we've observed instances where the Starter tier's free
limits were being used to run very large deployments and even entire sites.</p>
<p>While support is a key benefit we offer to paying customers, we believe everyone
deserves a positive FlowFuse experience. As such, we're adjusting the resource
limits to better align with the Starter tier's intended purpose and allow the
company to continue to invest in the Starter tier.</p>
<h2 id="transitioning-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/flowfuse-self-hosted-starter-resource-limits/#transitioning-users"></a> Transitioning Users</h2>
<p>If you're currently impacted by these resource limit changes, we want to ensure
a smooth transition. We're offering a complimentary 1-year Enterprise license to
affected users. To claim your license, simply email support@flowfuse.com with
the IP address your server has used to send telemetry data (if available) and a
screenshot of your admin panel so we can match the currently usage with the new
license.</p>
<p>We appreciate your understanding and continued support. If you have any
questions regarding this update, please don't hesitate to reach out to our team
at support@flowfuse.com.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/Why Low-Code is BetterStop coding in High-Code when it can be done in Low-Code2024-03-13T00:00:00Z<p>There are two common reasons why new languages come about. They provide a feature missing in the existing programming languages, or it is a tool that is easier to learn and use. The latter often functions like a swiss army knife with each iteration including more and more tools. The journey of low-code is like a swiss army knife, the perfect tool for the Citizen Developer.</p>
<!--more-->
<h2 id="a-typical-coding-evolution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/#a-typical-coding-evolution"></a> A Typical Coding Evolution</h2>
<p>Every decade seems to introduce something that simplifies coding. For me, in college, it was Python. A classmate was excited about this new programming language that was all about simplicity and readability, especially with its indentation-based syntax. It seemed too simple at first. Yet, over time, Python became a staple in my programming toolbox.</p>
<p>During this time in our education, our coursework was filled with languages like C++, which felt distant from the future of programming we imagined. We joked about "outdated" languages, not yet realizing the breadth of what programming could encompass.</p>
<p>After finishing college in 2010, I started working during the tail end of the housing crisis in the U.S. My first role was as a Controls System Integrator at Logical System Inc., where I was introduced to programming PLCs with Ladder Logic. Despite my initial reservations—viewing it as barely a programming language—this experience was my first step towards appreciating the diversity and utility of programming languages beyond the conventional.</p>
<p>Later on in my career, I worked as a Corporate Automation and Controls Engineer where I worked on and enforced standards for partner System Integrators and OEMs. One of these important standards was making sure applications written in the control process were written in Ladder Logic within the PLCs. There were exceptions, of course, but the rule was to apply a visually appealing coding language, Ladder Logic, over a text-like language often called Structured Text.</p>
<h2 id="programming-languages-as-specialized-tools" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/#programming-languages-as-specialized-tools"></a> Programming Languages as Specialized Tools</h2>
<p>Programming languages are typically optimized for certain tasks. For instance, Matlab and R excel in complex numerical computations thanks to their extensive libraries designed specifically for mathematical operations. While Python might not replace Matlab or R for their core functionalities, it can broaden the applicability of numerical analyses into various other contexts. In another example, VB.net is the go-to for standalone applications in Windows environments. However, for applications that need to run across different operating systems (excluding web applications), Java might be a better choice due to its platform independence. Each programming language has its niche, along with inherent complexities and constraints.</p>
<p>There are situations, however, where the specific strengths of a programming language become less critical. In cases where the application is straightforward, the choice of language might simply come down to personal preference or familiarity. But an important consideration arises when thinking about the future of the project: "Will someone else need to edit or view this code later?" If the answer is yes, and especially if the project aims to involve citizen developers, opting for a low-code solution becomes highly advantageous. Low-code platforms are designed with accessibility in mind, making them ideal for projects that benefit from collaboration and ease of maintenance.</p>
<h2 id="the-purpose-of-citizen-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/#the-purpose-of-citizen-development"></a> The Purpose of Citizen Development</h2>
<p>The idea behind Citizen Development is simple, make programming accessible to more people. This is what low-code platforms aim to do. They lower the entry barrier, making programming more inclusive. Insisting on complex, traditional programming languages when there are simpler, equally powerful alternatives seems counterproductive. We should be looking towards making programming more accessible to everyone.</p>
<p>If we want to drive meaningful change within our organizations, embracing tools that broaden participation is key. The aim of adopting new standards and tools is to simplify, not complicate. It's about finding better, more accessible ways to work that can accommodate a wider range of skill sets.</p>
<h2 id="simplification" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/#simplification"></a> Simplification</h2>
<p>This discussion isn't about the mechanics of coding, it's about opening up the field to more diverse contributions. Low-code platforms represent a step towards a more inclusive, collaborative future in technology. By lowering barriers to entry, we're not just simplifying coding, we're inviting a broader community to engage, innovate, and drive progress. I look forward to seeing how we can all contribute to this evolving landscape.</p>
<h2 id="how-flowfuse-helps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/low-code-is-better/#how-flowfuse-helps"></a> How FlowFuse Helps</h2>
<p>Our goal here at FlowFuse is to keep expanding on the Swiss army knife, Node-RED. We strive to elevate Node-RED for professionals by providing the tools needed to deploy Node-RED in a safe and secure way. For example by default, the editor for Node-RED is protected using your FlowFuse user credentials. You can also use SSO to further protect your user accounts and give access to Node-RED to your team members. 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.</p>
<p>We believe that Low-Code is the future and strive to make Citizen Development a reality. To learn more <a href="https://flowfuse.com/free-consultation/">schedule a call with one of our experts. </a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/Looking towards Node-RED 4.0 and beyondA look at what is coming in Node-RED 4.02024-03-07T00:00:00ZNick O'Leary<p>With Node-RED 4.0 coming soon, I wanted to take a look at what users can expect to see with the new release,
as well as some of the new features we're working on.</p>
<!--more-->
<p>The Node-RED project <a href="https://nodered.org/about/releases/">schedules its releases</a> around a yearly major release that coincides with when a Node.js version
reaches its end-of-support. This lets us drop support for that node.js version and update the default version of node used
in 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.</p>
<p>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.</p>
<p>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.</p>
<p>There are a few things already merged and ready to be released in the first beta release, and more landing each week.</p>
<h3 id="more-auto-complete" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#more-auto-complete"></a> More auto-complete</h3>
<p>Node-RED already has simple auto-complete on <code>msg</code> fields in the editor. We've now extended that to also work with <code>flow</code>/<code>global</code> context inputs as well as the <code>env</code> type for accessing environment variables.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr4-auto-complete-14i7D5DBpC-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr4-auto-complete-14i7D5DBpC-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Node-RED editor autocompleting properties" alt="Node-RED editor autocompleting properties" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr4-auto-complete-14i7D5DBpC-650.jpeg" width="650" height="184" /></picture></p>
<p>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.</p>
<p>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.</p>
<p>The <code>msg</code> 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.</p>
<h3 id="timestamp-formatting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#timestamp-formatting"></a> Timestamp formatting</h3>
<p>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.</p>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr4-timestamp-formatting-1QFHkE6yLW-582.avif 582w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr4-timestamp-formatting-1QFHkE6yLW-582.webp 582w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Format options for Node-RED timestamp" alt=""Format options for Node-RED timestamp"" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr4-timestamp-formatting-1QFHkE6yLW-582.jpeg" width="582" height="272" /></picture></p>
<ul>
<li><em>milliseconds since epoch</em> - the existing option, just more explicitly labelled for what it is</li>
<li><em>YYYY-MM-DDTHH:mm:ss.sssZ</em> - also known as ISO 8601</li>
<li><em>JavaScript Date Object</em> - the standard Date object</li>
</ul>
<p>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.</p>
<h3 id="a-better-csv-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#a-better-csv-node"></a> A better CSV node</h3>
<p>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.</p>
<p>The new node follows the <a href="https://www.ietf.org/rfc/rfc4180.txt">RFC4180</a> standard and is also faster - wins all around.</p>
<p>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.</p>
<h3 id="customising-config-nodes-in-subflows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#customising-config-nodes-in-subflows"></a> Customising config nodes in Subflows</h3>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<h3 id="updated-jsonata" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#updated-jsonata"></a> Updated JSONata</h3>
<p>The JSONata library is used to provide the <code>expression</code> 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.</p>
<h3 id="and-many-more-minor-changes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#and-many-more-minor-changes"></a> And many more minor changes</h3>
<p>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.</p>
<h2 id="looking-further-ahead" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#looking-further-ahead"></a> Looking further ahead</h2>
<p>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.</p>
<p>I wrote recently about improving how users can <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#testing">test their flows</a>. 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.</p>
<h3 id="concurrent-editing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#concurrent-editing"></a> Concurrent editing</h3>
<p>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.</p>
<p>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.</p>
<p>Our goal is to make collaboration as simple and natural as possible.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<h2 id="beta-releases" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/looking-towards-node-red-4/#beta-releases"></a> Beta releases</h2>
<p>The <a href="https://nodered.org/about/releases/">release plan</a> 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.</p>
<p>Keep an eye on the <a href="https://discourse.nodered.org/c/news/9">community forum</a> for release announcements as they come.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/installing-operating-node-red-behind-firewall/Installing and operating Node-RED behind a firewallFlowFuse was built to empower Node-RED to run everywhere, even behind a firewall2024-03-06T00:00:00ZZJ van de Weg<p>Practitioners using Node-RED often find themselves in a situation where a firewall
is 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.</p>
<!--more-->
<h2 id="node-red-installation-with-a-firewall" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/installing-operating-node-red-behind-firewall/#node-red-installation-with-a-firewall"></a> Node-RED installation with a firewall</h2>
<p>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.</p>
<p>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 <code>apt-get</code> on Debian/Ubuntu-based systems, or there’s a marketplace approach to install Node-RED like for example the <a href="https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135">Rexroth CtrlX with Node-RED in it</a>. 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.</p>
<h2 id="flowfuse-and-your-firewall" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/installing-operating-node-red-behind-firewall/#flowfuse-and-your-firewall"></a> FlowFuse and your firewall</h2>
<p>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.</p>
<p>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 <a href="https://flowfuse.com/certified-nodes/">“Certified Nodes” catalog</a>. These nodes have gone through automated and manual inspection to prevent malicious code from making it onto your production systems.</p>
<p>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.</p>
<p>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.</p>
<h2 id="wrap-up" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/installing-operating-node-red-behind-firewall/#wrap-up"></a> Wrap up</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/history-of-nodered/History of Node-REDHow it all started as told by Node-RED creator Nick O'Leary2024-02-28T00:00:00ZNick O'Leary<p>In January 2013, I could have never foreseen that my fun little proof-of-concept project would become Node-RED, an open source low-code environment with millions of deployments in IoT and automation.</p>
<!--more-->
<p>Long before IoT became the ubiquitous term it is today, I was working in IBM’s Emerging Technology Group playing around with capturing data from devices and doing interesting things with it. The team focused on very fast-paced, short, proof-of-concept projects and was afforded time to learn new skills, innovate and work on side projects.</p>
<p>My background working with the MQTT protocol space before it was known outside of IBM led me to a side project: I wanted some way to visualize mapping messages on an MQTT infrastructure to see how they come in on one topic and get sent out on another.</p>
<p>Using it as an excuse to also start playing the relatively new Node.js runtime, I spent a day or two putting together a little demo of an application that would connect to an MQTT broker and visualize the topic mappings in a web browser.</p>
<p>Showing it to my colleague, Dave Conway-Jones, I mentioned it wouldn't take much to make it more interactive; to let you draw the mappings and apply them. He sent me on my way to do just that and, 24 hours later, I had a simple browser-based application that could define and apply mappings between MQTT topics.</p>
<p>It very quickly became useful.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/history-nr-screenshot-suha6UufnL-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/history-nr-screenshot-suha6UufnL-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/history-nr-screenshot-suha6UufnL-650.jpeg" width="650" height="495" /></picture></p>
<p class="italic" style="font-size: 0.9em; margin-top: -1rem;">An early screenshot of Node-RED</p>
<p>The projects Dave and I were working on became rich sources of requirements; each project needing to access data from some other source such as a device plugged into a serial port, or being able to modify the data with a bit of JavaScript code.</p>
<p>I spent a few days redesigning the code to make it easier to write in new nodes, unlocking the ability to quickly add in the function node, change node, and switch node, which became the basic building blocks of the tool.</p>
<p>The utility of the application was clear, and more colleagues started making use of it - all grounded in real client projects. But it was still a tool largely known only to our team.</p>
<h3 id="going-open-source" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/history-of-nodered/#going-open-source"></a> Going Open Source</h3>
<p>As we considered how best to move the project forward, we essentially had two choices; keep it to ourselves and see if we could get an IBM product group to back turning it into a fully-fledged product, or to go the open source route. In my mind, the OSS route was the natural fit and that is what we ultimately chose to do, getting it published in late 2013.</p>
<p>I demoed Node-RED in late 2013 at a London IoT meetup and word spread among my peers in that community. A week later, I was at an open source hardware conference and attended a workshop on home automation. I was surprised to see Node-RED on everyone’s screens! The facilitator had seen Node-RED and reworked his workshop so that people didn’t have to worry about writing lines of code and were able to do useful things much more quickly.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-650.avif 650w, https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-650.webp 650w, https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-650.jpeg 650w, https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/history-ibm-lab-P2wH0a6IKn-650.jpeg" width="1300" height="975" /></picture></p>
<p class="italic" style="font-size: 0.9em; margin-top: -1rem;">The IBM Emerging Technology Demo Lab - many of the demos were built on Node-RED</p>
<h3 id="building-an-audience" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/history-of-nodered/#building-an-audience"></a> Building an audience</h3>
<p>A key step forward was when IBM was preparing to launch its new IBM Cloud service. In the months before the launch, they were looking for innovative ideas that could help expand the offering. We put forward a proposal to use this tool we'd created as a way to visualise the mapping of web services within the cloud.</p>
<p>This generated some great interest from a wider audience within the company, and whilst that concept didn't ultimately come to anything, we had gotten our project noticed.</p>
<p>Over time, we started seeing Node-RED being picked up by more than just the OSS community. Companies started using it with their own hardware devices and online services.</p>
<p>By this time, IBM Cloud had launched, and from our previous conversations with them, we got Node-RED included as one of the 'starter applications' in the catalogue that gave users a one-click option for getting Node-RED running in the cloud.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-650.avif 650w, https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-650.webp 650w, https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-650.jpeg 650w, https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/history-cloud-catalog-ya8a2Mbcsr-650.jpeg" width="1300" height="454" /></picture></p>
<p class="italic" style="font-size: 0.9em; margin-top: -1rem;">Node-RED in the original IBM Cloud catalog</p>
<h3 id="moving-to-a-foundation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/history-of-nodered/#moving-to-a-foundation"></a> Moving to a Foundation</h3>
<p>As we saw the project grow, discussions were had around the longer-term future of the project. Some companies voiced a concern about it being a single-vendor open source project. This also came at a time when IBM was actively working with the Node.js project to help relaunch the Node Foundation (which has since become the OpenJS Foundation). This culminated in Node-RED joining the foundation as one of its founding projects, alongside other well-established projects such as Node.js itself, jQuery and many others.</p>
<p>Having an independent governance structure around the project gave companies more confidence to get involved, knowing they had an equal voice in its development. Hitachi became big supporters of the project and had a team dedicated to working on it.</p>
<p>For software developers, time spent writing boilerplate code is not time adding value to the application they’re building. With low-code, Node-RED abstracts all that boilerplate so they can focus on the business problem.</p>
<p>Device manufacturers paid attention when Node-RED was installed on the Raspberry Pi image, with its low-code accessibility attracting a broad range of people from systems engineers building automations to IoT hobbyists.</p>
<p>Now with millions of deployments, Node-RED continues to collect, transform, and integrate data through visualized dashboards. And, as this open source community grows it remains rooted in the two pillars of its low-code user experience, and its extensibility.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/Citizen Development: Unleashing Domain ExpertsWhy you should encourage domain experts to build applications2024-02-26T00:00:00Z<p>Citizen development has taken the spotlight recently, but the concept itself isn't entirely new. Remember the era of "hackers"? They weren't malicious actors, but individuals who tinkered with technology, pushing boundaries and creating solutions. Today, these same problem-solving personas have evolved into "<a href="https://www.gartner.com/en/information-technology/glossary/citizen-developer">citizen developers</a>" – business users empowered to build applications without relying solely on professional coders.</p>
<!--more-->
<h2 id="why-now%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/#why-now%3F"></a> Why Now?</h2>
<p>While the drive for user-friendliness has always been present in programming languages, several factors have converged to push citizen development to the forefront:</p>
<ul>
<li>Democratization of Technology: Cloud computing and advancements in low-code/no-code platforms have lowered the barrier to entry, making development tools accessible to individuals with minimal technical expertise.</li>
<li>Business Agility: The need for rapid innovation necessitates solutions that bypass lengthy IT backlogs. Citizen developers can bridge the gap, creating applications and internal tools quickly and efficiently.</li>
<li>Domain Expertise: Business users possess deep insights into specific processes and challenges. By giving them the tools to build solutions, organizations can tap into a new knowledge source by putting domain experts into the driver's seat.</li>
</ul>
<h3 id="beyond-spreadsheets%3A-platforms-of-empowerment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/#beyond-spreadsheets%3A-platforms-of-empowerment"></a> Beyond Spreadsheets: Platforms of Empowerment</h3>
<p>Reports and Dashboards are often created by a BI teams that have to take information from specialists to create custom visualizations that may or may not come out to be exactly what they asked for. Queue old faiful, spreadsheets, where a domain expert can create their own reports that suit their needs. Spreadsheets are powerful for analysis, but they often lack the real-time secure connectivity needed for enterprise applications. Transform the knowledge created in a spreadsheet into a personalized real-time visualization. To do that, you must first select a citizen development platform and those platforms should offer:</p>
<ul>
<li>Visual Interfaces: Drag-and-drop functionality and pre-built components eliminate the need for complex coding, allowing users to focus on logic and functionality.</li>
<li>Integration Capabilities: Seamless connection with existing data sources and systems ensures that citizen-built applications integrate seamlessly into the overall workflow. Plus, the ability to create custom integrations.</li>
<li>Governance and Security: IT governance establishes guardrails while empowering users, ensuring data security and application stability through features like Role Based Access Control and SSO integration.</li>
<li>Collaboration Tools: Built-in collaboration features enable teams to share ideas, iterate on solutions, and ensure knowledge transfer.</li>
<li>Reseliency: Backup management.</li>
</ul>
<h3 id="citizen-developers%3A-not-rube-goldberg-machines%2C-but-value-creators" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/#citizen-developers%3A-not-rube-goldberg-machines%2C-but-value-creators"></a> Citizen Developers: Not Rube Goldberg Machines, But Value Creators</h3>
<p>The key to successful citizen development lies in empowerment, not abdication. By providing the right tools, training, and governance, organizations can avoid creating complex, fragile solutions. Instead, citizen developers become powerful problem-solvers, building value-driven applications that address specific needs and contribute to overall business goals.</p>
<h2 id="lean-in" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/why-citizen-development-platforms/#lean-in"></a> Lean In</h2>
<p>Remember, citizen development isn't about replacing professional developers. It's about creating a collaborative environment where everyone can contribute their unique skills and perspectives. By removing the coding barrier, we unleash a wider pool of innovators, accelerating progress and driving organizational success.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/Storing Data: Getting Started with Node-REDNode-RED is one of the easiest ways to program ever created but everyone needs a little help2024-02-19T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>In this article, we will explore some of the better solutions for storing and retrieving transactional information in your flows.</p>
<h4 id="storing-data-outside-of-msg.payload" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/#storing-data-outside-of-msg.payload"></a> Storing data outside of msg.payload</h4>
<p>In this example, we have data in msg.payload as well as in msg.later.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/data-outside-msg-payload-SMjDhqJt-E-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Storing data outside of msg.payload" alt="Storing data outside of msg.payload" loading="lazy" decoding="async" src="https://flowfuse.com/img/data-outside-msg-payload-SMjDhqJt-E-650.webp" width="650" height="226" /></picture></p>
<p>If you want your debug to display the full content of the message, change the output to 'complete message object' as shown above.</p>
<p>You can import the flow using this code.</p>
<div id="nr-flow-131" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow131 = "\n[{\"id\":\"aaa1e17e5f158004\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Inject the message\",\"props\":[{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow131.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-131') })</script>
<p>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.</p>
<h4 id="tidying-your-messages" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/#tidying-your-messages"></a> Tidying your messages</h4>
<p>You may also want to remove data you don't need from your messages to optimize the speed of your flows.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/delete-other-N0V0b_jWnm-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Deleting data from msg.other" alt="Deleting data from msg.other" loading="lazy" decoding="async" src="https://flowfuse.com/img/delete-other-N0V0b_jWnm-650.webp" width="650" height="196" /></picture></p>
<p>The Change Node is configured as follows.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/delete-EbnbfRJ_27-561.avif 561w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/delete-EbnbfRJ_27-561.webp 561w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Change Node configuration" alt="Change Node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/delete-EbnbfRJ_27-561.jpeg" width="561" height="290" /></picture></p>
<p>You can import the flow using this code.</p>
<div id="nr-flow-132" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow132 = "\n[{\"id\":\"1ac51e71153f7c1f\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Inject the message\",\"props\":[{\"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\":[]},{\"id\":\"cd7609101328caa2\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":[{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow132.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-132') })</script>
<h4 id="storing-data-outside-of-msg.payload-so-you-can-access-it-later-in-your-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/#storing-data-outside-of-msg.payload-so-you-can-access-it-later-in-your-flows"></a> Storing data outside of msg.payload so you can access it later in your flows</h4>
<p>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.</p>
<p>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.</p>
<p>You can see the flow working below.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/sunset-example-jsDwOaJQKU-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example flow which gets the sunset time for a given location" alt="Example flow which gets the sunset time for a given location" loading="lazy" decoding="async" src="https://flowfuse.com/img/sunset-example-jsDwOaJQKU-650.webp" width="650" height="254" /></picture></p>
<p>You can import the flow using this code.</p>
<div id="nr-flow-133" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow133 = "\n[{\"id\":\"809cc8f4678767b7\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"London\",\"props\":[{\"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\":[{\"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\":[]},{\"id\":\"6b5a6a2ef7a64f1a\",\"type\":\"change\",\"z\":\"67746003c844dbc4\",\"name\":\"\",\"rules\":[{\"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\":\"https://api.sunrisesunset.io/json?lat={{store.lat}}&lng={{store.lng}}\",\"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\":[]},{\"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\":[],\"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\":[]},{\"id\":\"a245b322ade3a7e9\",\"type\":\"template\",\"z\":\"67746003c844dbc4\",\"name\":\"Create the sentence\",\"field\":\"payload\",\"fieldType\":\"msg\",\"format\":\"handlebars\",\"syntax\":\"mustache\",\"template\":\"The sun will set at in .\",\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow133.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-133') })</script>
<p>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.</p>
<h4 id="why-not-store-the-values-in-context%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/#why-not-store-the-values-in-context%3F"></a> Why not store the values in context?</h4>
<p>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 <a href="https://en.wikipedia.org/wiki/Race_condition#:~:text=A%20race%20condition%20or%20race,to%20unexpected%20or%20inconsistent%20results">race condition</a> within your flow that could result in data corruption.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/race-condition-YyJKcqFYsf-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example of how a race condition can make context a bad place to cache data" alt="Example of how a race condition can make context a bad place to cache data" loading="lazy" decoding="async" src="https://flowfuse.com/img/race-condition-YyJKcqFYsf-650.webp" width="650" height="222" /></picture></p>
<p>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.</p>
<p>You can import the flow using this code.</p>
<div id="nr-flow-134" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow134 = "\n[{\"id\":\"7ac4e7165d99f041\",\"type\":\"inject\",\"z\":\"67746003c844dbc4\",\"name\":\"Rob\",\"props\":[{\"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\":[{\"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\":[{\"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\":[{\"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\":[]},{\"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\":[{\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow134.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-134') })</script>
<h4 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/taking-it-further-with-node-red/#conclusion"></a> Conclusion</h4>
<p>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.</p>
<p>Thanks to <a href="https://sunrisesunset.io/">SunriseSunset</a> for creating the useful API we've used in this article.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-unified-namespace-architecture/Node-RED in a Unified Namespace ArchitectureHow does Node-RED elevate a Unified Namespace Architecture?2024-02-14T00:00:00Z<p>As we embark on the journey toward a more interconnected industrial environment, the emergence of a Unified Namespace (UNS) as the fundamental framework for facilitating communication among various systems and devices has become a focal point of discussion. I've often been queried about the role of Node-RED in a UNS architecture. To illuminate this, let's delve into an exemplary UNS architecture, underlining the classic use cases for Node-RED within this framework.</p>
<!--more-->
<p>In this Article: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/">Node-RED: The perfect adapter and middleware for your UNS</a>, where classic use cases for Node-RED are delineated. Building upon that foundation, this discussion aims to present a tangible architectural example, showcasing the practical implementation of these use cases.</p>
<h2 id="simplifying-complexity%3A-the-two-layered-approach" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-unified-namespace-architecture/#simplifying-complexity%3A-the-two-layered-approach"></a> Simplifying Complexity: The Two-Layered Approach</h2>
<p>For ease of understanding, consider the architecture split into two principal layers: the Shopfloor layer and the Service layer.</p>
<p><strong>The Shopfloor Layer</strong>
This is where the physical assets dwell, requiring connectivity through a network layer, which in our example, is the UNS. It acts as the foundation for data flow from the operational technology (OT) on the shop floor to the information technology (IT) in the Service layer.</p>
<p><strong>The Service Layer</strong>
Here resides the applications and software that analyze data, transforming raw metrics into actionable insights. It's where the data becomes meaningful through analytics, dashboards, and decision-support tools.</p>
<h2 id="the-roles-in-uns-architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-unified-namespace-architecture/#the-roles-in-uns-architecture"></a> The Roles in UNS Architecture</h2>
<p>Within this bifurcated architecture, we have two general categories of actors: Indirect Consumer/Producers and Direct Consumer/Producers.</p>
<p><strong>Indirect Consumer/Producers</strong>:
These actors cannot natively communicate with our UNS broker. The communication barrier could be due to protocol differences, such as not using MQTT, or payload structures incompatible with your UNS's schema. This is a common challenge in manufacturing, especially in "brownfield" scenarios where legacy machines and equipment from various eras must be integrated.</p>
<p>In such cases, Node-RED shines as a middleware for protocol conversion and data contextualization. Take, for example, the topic hierarchy in UNS based on location for context. A raw sensor reading might lack necessary details like measurement units or message versions. Node-RED steps in to enrich this data, ensuring compatibility with the UNS. In our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/">concrete architecture example</a>, we have an indirect PLC producer. With Node-RED, we can convert and contextualize the data from this PLC for the UNS, ensuring smooth communication and effective integration.</p>
<p><strong>Direct Consumer/Producers</strong>:
Contrastingly, direct actors can interact with the UNS out of the box. Modern industrial equipment usually falls into this category, equipped to speak the language of the UNS directly. However, the challenge remains not just in protocol communication but also in data contextualization. Merely speaking the same language is not enough; the data must also carry the correct context to be fully understood and utilized.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-650.avif 650w, https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-650.webp 650w, https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-650.jpeg 650w, https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Example Architecture" loading="lazy" decoding="async" src="https://flowfuse.com/img/unified-namespace-architecture-hUxpOvAc1R-650.jpeg" width="1300" height="731" /></picture></p>
<h2 id="harnessing-node-red-for-actionable-insights" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-unified-namespace-architecture/#harnessing-node-red-for-actionable-insights"></a> Harnessing Node-RED for Actionable Insights</h2>
<p>Node-RED's prowess extends beyond middleware capabilities; it can also derive actionable insights. Our example architecture includes Dashboards for both the Human Machine Interface on the Shopfloor and an OEE Dashboard in the Service Layer. These dashboards engage with the UNS, calculating KPIs directly within Node-RED. For manufacturing applications, our <a href="https://flowfuse.com/blueprints/">Blueprint Library</a> serves as a robust starting point, offering one-click deployment to your Node-RED managed instance.
Node-RED emerges not just as a translator between machines and UNS but as an interpreter and analyst, generating real-time insights that drive decision-making and operational efficiency. The role of Node-RED in UNS architecture, therefore, is not ancillary; it is central to realizing the vision of a connected, intelligent industrial ecosystem.</p>
<p><img alt="Andon Live Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/ANDON-Screenshot-D4DBvWieJZ-650.avif" width="undefined" height="undefined" /></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/Connect Node-RED to KepserverEX OPC server.Step by step guide to connect to PTC's Kepware OPC server.2024-02-12T00:00:00Z<p>KepserverEX, often referred to as Kepware, is an OPC server that has been the important tool many manufacturing companies have used on their digital transformation journey. It plays an important role for many to extract data from PLCs, Programmable Logic Controllers, without having to directly interact with them.</p>
<!--more-->
<h2 id="ptc's-kepserverex" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#ptc's-kepserverex"></a> PTC's KepserverEX</h2>
<p>PTC's <a href="https://www.ptc.com/en/products/kepware/kepserverex-ppc">KEPServerEX</a> is a versatile connectivity platform designed to securely manage, monitor, and control diverse automation devices and software applications. Central to its functionality is the OPC standard, which enables universal communication across industrial hardware and software, facilitating data exchange. This makes KEPServerEX particularly valuable in a variety of use cases, such as real-time data monitoring, machine-to-machine (M2M) communication, and industrial Internet of Things (IIoT) applications. It serves as a critical bridge in the automation and controls engineering space, offering a robust solution for integrating disparate systems, thereby enhancing operational efficiency and enabling data-driven decision-making. Integrating KEPServerEX with Node-RED extends this functionality, by allowing bidirectional communication for sending, storing, and or manipulating data.</p>
<h3 id="scope" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#scope"></a> Scope</h3>
<p>The goal of this blog is for a quick start guide on the configuration for collecting data from a KepserverEX OPC server. We are going to be leveraging the <a href="https://flows.nodered.org/node/node-red-contrib-opcua">node-red-contrib-opcua</a> node. We will assume that you already have <a href="https://www.ptc.com/en/products/kepware/kepserverex-ppc">KepserverEX</a> install and ready for the integration. We will be using Basic256Sha256 security in this guide with anonymous authentication. Assumptions of the installation include allowing Default configuration of the installation of KepserverEX 6.15 and allowing dynamic tag addressing.</p>
<h3 id="configure-connection-from-node-red-to-kepserver" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#configure-connection-from-node-red-to-kepserver"></a> Configure Connection from Node-RED to Kepserver</h3>
<h4 id="step-1%3A-kepserverex" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#step-1%3A-kepserverex"></a> Step 1: KepserverEX</h4>
<p>The first thing we need to do is check our <strong>OPC UA Configuration Manager</strong> for the security requirements for our environment. In the tray at the bottom, click on the KepserverEX symbol and select <strong>OPC UA Configuration</strong></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/kepserverex-tray-QqmK2jUgGh-219.avif 219w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kepserverex-tray-QqmK2jUgGh-219.webp 219w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware tray" loading="lazy" decoding="async" src="https://flowfuse.com/img/kepserverex-tray-QqmK2jUgGh-219.jpeg" width="219" height="326" /></picture></p>
<p>If your Node-RED instance lives on the same server that your KepserverEX is on, pick accordingly or click add if you need to define by ip address. This is for setting different credential requirements for localhost vs remote host access. Also note, that if you have multiple network adapters, make sure to select the adapter that is in use.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/kep-endpoint-definition-MV8GQdyozv-543.avif 543w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kep-endpoint-definition-MV8GQdyozv-543.webp 543w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware endpoint definition" loading="lazy" decoding="async" src="https://flowfuse.com/img/kep-endpoint-definition-MV8GQdyozv-543.jpeg" width="543" height="347" /></picture></p>
<p>We are testing locally on the server, so we will use the one selected for loopback addressing. We will be leaving the OPC server port as default and select <strong>Basic256Sha256</strong> with <strong>Sign and Encrypt</strong>.</p>
<p>Click <strong>OK</strong>.</p>
<h4 id="step-2%3A-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#step-2%3A-node-red"></a> Step 2: Node-RED</h4>
<p>Next, navigate to your Node-RED instance and install the <a href="https://flows.nodered.org/node/node-red-contrib-opcua">node-red-contrib-opcua</a> node if you haven't already done so.</p>
<p>Import the flow below into your Node-RED environment.</p>
<iframe width="100%" height="225px" src="https://flows.nodered.org/flow/04a84fe5b0db7cda9e74ba811e7b0ca5/share?height=250" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<p>Next, let's configure the <strong>OPC UA Client</strong>. Click the <strong>pencil</strong> to add a new OPCUA-Endpoint.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-endpoint-node-red-encrypted-pnhgwT-Uka-510.avif 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-endpoint-node-red-encrypted-pnhgwT-Uka-510.webp 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red encrypted opc ua node" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-endpoint-node-red-encrypted-pnhgwT-Uka-510.jpeg" width="510" height="425" /></picture></p>
<p>For the endpoint, <strong>copy</strong> the endpoint definition from the KepserverEX OPC UA Configuration Manager. In our example, it is <code>opc.tcp://127.0.0.1:49320</code>, and paste it into the Endpoint. For SecurityPolicy select <strong>Basic256Sha256</strong>. For SecurityMode, select <strong>Sign&Encrypt</strong>. Lastly, we will be selecting <strong>Anonymous</strong>. Click <strong>Update</strong>, then <strong>Deploy</strong>.</p>
<p>Trigger the flow by <strong>clicking</strong> on the inject node. The server may not connect at this time, and it is expected.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-opc-ua-invalid-endpoint-t5uZPH6xlb-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-opc-ua-invalid-endpoint-t5uZPH6xlb-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red invalid endpoint" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-opc-ua-invalid-endpoint-t5uZPH6xlb-650.jpeg" width="650" height="91" /></picture></p>
<h4 id="step-3%3A-kepserverex" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#step-3%3A-kepserverex"></a> Step 3: KepserverEX</h4>
<p>Moving back over to KepserverEX, Click on the tray again in the bottom of the screen and select <strong>Configuration</strong>, then select <strong>Edit</strong> from the file menu, then <strong>Properties</strong>. Next, Select <strong>OPC UA</strong> and ensure that <strong>Allow Anonymous login</strong> is set to <strong>Yes</strong>. Click <strong>OK</strong>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-kepware-anonymous-login-3B-EJsJH6O-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-kepware-anonymous-login-3B-EJsJH6O-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red anonymous login" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-kepware-anonymous-login-3B-EJsJH6O-650.jpeg" width="650" height="385" /></picture></p>
<p>Select the tray at the bottom of the screen again, and select <strong>OPC UA Configuration</strong>. Select the <strong>Trusted Clients</strong> tab.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/kepserverex-trusted-client-before-pVmF0OD14u-618.avif 618w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kepserverex-trusted-client-before-pVmF0OD14u-618.webp 618w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red trusted client before" loading="lazy" decoding="async" src="https://flowfuse.com/img/kepserverex-trusted-client-before-pVmF0OD14u-618.jpeg" width="618" height="456" /></picture></p>
<p>Now select the <strong>NodeOPCUA-Client</strong> and then <strong>click</strong> Trust. <em><strong>If you don't have the client option, trigger the inject node again from the Node-RED flow and check the logs</strong></em></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/kepserverex-trusted-client-after-C9-aKvQ9hf-621.avif 621w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kepserverex-trusted-client-after-C9-aKvQ9hf-621.webp 621w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red trusted client after" loading="lazy" decoding="async" src="https://flowfuse.com/img/kepserverex-trusted-client-after-C9-aKvQ9hf-621.jpeg" width="621" height="463" /></picture></p>
<h4 id="step-4%3A-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#step-4%3A-node-red"></a> Step 4: Node-RED</h4>
<p>Lastly, Navigate back to Node-RED and <strong>trigger</strong> the inject node. This node will now browse the project from the KepserverEX and display all of the existing tags.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/kepware-opc-browsed-tags-node-red-9I-AK21K3k-460.avif 460w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kepware-opc-browsed-tags-node-red-9I-AK21K3k-460.webp 460w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red browsed tags" loading="lazy" decoding="async" src="https://flowfuse.com/img/kepware-opc-browsed-tags-node-red-9I-AK21K3k-460.jpeg" width="460" height="596" /></picture></p>
<h3 id="read-tags" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#read-tags"></a> Read Tags</h3>
<p>We will be leveraging the default Simulated Examples for reading tags from KepserverEX. Let's move on to the next set of flows.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-kepware-read-tag-5xMr3AhvM7-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-kepware-read-tag-5xMr3AhvM7-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red read tags" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-kepware-read-tag-5xMr3AhvM7-650.jpeg" width="650" height="61" /></picture></p>
<p>Edit the OPCUa-Item node and note the item.</p>
<pre><code>ns=2;s=Simulation Examples.Functions.Ramp1
</code></pre>
<p>Let's break down the syntax, ns stands for namespace that will coincide with the project. In this case, it is namespace 2. Once the namespace has been selected, we are use <strong>Dynamic Addressing</strong> to select the tag through the variable <strong>s</strong>, which stands for string type of NodeId. <strong>Click</strong> Done. Now let's <strong>trigger</strong> the Read inject node and view the debug output.</p>
<p>The debug node is set to show a complete msg object. Note the payload as the value of the variable.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-debug-output-opc-uiECBwJsXC-473.avif 473w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-debug-output-opc-uiECBwJsXC-473.webp 473w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red read tags output" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-debug-output-opc-uiECBwJsXC-473.jpeg" width="473" height="278" /></picture></p>
<h3 id="write-tag" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#write-tag"></a> Write Tag</h3>
<p>Writing a tag is a similar process. The only difference is that a variable is set in the <strong>OPCUa-Item</strong> node and the <strong>OPCUa-Client</strong> action is set to Write.</p>
<p>In this example, we created a new variable in KepserverEX under <strong>Simulation Examples > Functions</strong> called myInt of Date Type Long.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-kepware-write-tag-cg08zZsS9T-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-kepware-write-tag-cg08zZsS9T-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red write tags" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-kepware-write-tag-cg08zZsS9T-650.jpeg" width="650" height="69" /></picture></p>
<p>View the OPCUa-Item node and note the item.</p>
<pre><code>ns=2;s=Simulation Examples.Functions.myInt
</code></pre>
<p><strong>Click</strong> Done and <strong>Deploy</strong></p>
<p>Open up the <strong>Quick Client</strong> within KepserverEX and navigate to the address of <strong>Simulation Examples.Functions</strong> and look for myInt. It should by default, be 0. <strong>Trigger</strong> the inject node within Node-RED to see the Value change within the Quick Client.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-debug-output-write-opc-1-Tfg-UK3T-485.avif 485w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-debug-output-write-opc-1-Tfg-UK3T-485.webp 485w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red write tags output" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-debug-output-write-opc-1-Tfg-UK3T-485.jpeg" width="485" height="321" /></picture>
<picture><source type="image/avif" srcset="https://flowfuse.com/img/kepware-quick-client-lh5PnZay82-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/kepware-quick-client-lh5PnZay82-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="kepware node-red quick client" loading="lazy" decoding="async" src="https://flowfuse.com/img/kepware-quick-client-lh5PnZay82-650.jpeg" width="650" height="214" /></picture></p>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/connect-node-red-to-kepware-opc/#conclusion"></a> Conclusion</h3>
<p>This guide was designed to help you easily connect your Node-RED instance to KepserverEX with security. For more examples of how to do more advanced configuration, please watch the past <a href="https://flowfuse.com/webinars/2023/getting-started-opcua-node-red/">webinar</a> going over these <a href="https://github.com/mikakaraila/node-red-contrib-opcua/tree/master/examples">examples</a> in detail.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/Bringing Software Development practices to Node-REDApplying lessons from traditional development to the low-code space.2024-02-06T00:00:00ZNick O'Leary<p>I'm always thinking about how we can continue to improve the Node-RED experience. One area I like to explore is to make sure we learn the right lessons from the Software Development world.</p>
<p>In this post, I'm going to look at some of the common practices in modern Software Development and show how they translate to the Node-RED world.</p>
<!--more-->
<h3 id="linting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#linting"></a> Linting</h3>
<p>Linting is a way of examining some code and automatically spotting things that need attention. This can range from stylistic errors ("Use tabs, not spaces") to real bugs that will prevent the code from working as intended.</p>
<p>This is all about spotting problems <em>before</em> the code runs. It also helps ensure consistency when you have multiple people contributing to the code.</p>
<p>Having code that is consistently formatted and free of syntactic mistakes makes it much easier to maintain.</p>
<p>Applying this concept to Node-RED, we have the <a href="https://github.com/node-red/nrlint"><code>nrlint</code> tool</a>. This is a linting tool that can run either on the command-line or within the editor directly to spot potential problems with the flows.</p>
<p>On the stylistic side, for example, it can highlight nodes that aren't properly aligned to the grid. Whilst this doesn't have any bearing on the runtime operation of the flow, it encourages keeping the flows tidy and orderly.</p>
<p>It can help identify potential infinite loops in flows, and highlight Debug nodes without a name set.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-linter-AIfk_2k73f-650.avif 650w, https://flowfuse.com/img/node-red-linter-AIfk_2k73f-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-linter-AIfk_2k73f-650.webp 650w, https://flowfuse.com/img/node-red-linter-AIfk_2k73f-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/node-red-linter-AIfk_2k73f-650.jpeg 650w, https://flowfuse.com/img/node-red-linter-AIfk_2k73f-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-linter-AIfk_2k73f-650.jpeg" width="1300" height="367" /></picture></p>
<h3 id="debugging" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#debugging"></a> Debugging</h3>
<p>Whether you are writing lines of code or not, eventually you will need to figure out why your application isn't doing what you think it should.</p>
<p>In Software Engineering there are two typical approaches. One is to add debug statements through the code to print out bits of information as the program runs. Then, depending on what output you got, you'd move the debug statements around, add some more, print out different bits of information - all until you'd nailed down the problem. This is the Debug node approach in Node-RED; adding nodes at different points of your flow to capture some piece of information and then iterating as you go.</p>
<p>This is probably how most Node-RED users go about it today. The downside is you end up leaving the Debug nodes in place, capturing information long after it is needed.</p>
<p>The alternative approach is Step-by-Step debugging. This is why you are able to pause the program and then step it forward one statement at a time - examining the state at each point. But what's the equivalent for low-code? Pretty much exactly that when you have the <a href="https://flows.nodered.org/node/node-red-debugger">Node-RED Debugger plugin</a> installed.</p>
<p>This allows you to set 'breakpoints' on any node input or output that are triggered when a message arrives at that point of the flow. The Debugger will then pause the whole runtime and shows you all of the queued up messages in the flow.</p>
<p>You can then examine those messages and tell the Debugger to 'release' them one at a time - seeing how the flow progresses.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-650.avif 650w, https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-650.webp 650w, https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-650.jpeg 650w, https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-debugger-D38_PJq6FA-650.jpeg" width="1300" height="695" /></picture></p>
<h3 id="testing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#testing"></a> Testing</h3>
<p>Testing code is a critical part of developing software. You want to make sure it does what you want. But it's more than just manually testing it once and then letting it go; you want to have tests you can run regularly, whenever you make changes, to ensure you don't break something that was working previously.</p>
<p>In the Software Development world, there are all sorts of testing methodologies and techniques; unit testing individual components, system testing larger sections, stubbing out components to simulate different conditions, integrating test suites into the whole development process.</p>
<p>They each have their own place in the process of software development. The question is, how does this apply to Node-RED?</p>
<p>Most Node-RED users today will of course be testing their flows whilst developing them - iterating until the flow does what is needed. It's far less common to have a set of repeatable tests including the flows.</p>
<p>That is certainly achievable with Node-RED today, albeit with some limitations. For example, a typical test will be to verify that, given a set of particular inputs, the outputs look correct. This can be done using Inject nodes to quickly trigger messages with different values, and use Debug nodes to examine the results.</p>
<p>That isn't ideal as you end up littering your flows with these extra Inject nodes, and it still requires manually verifying the results. It also doesn't work well if your flows need to interact with external systems - such as saving values to a database. You don't want to pollute your system with test data.</p>
<p>So what would be the ideal workflow? This is something the project has spent some time exploring in the past, and the start of a design was put together.</p>
<p>The concept would be to introduce a Testing sidebar to the editor. Within that, you can define a set of test cases. Then for each test case, you can customise the behaviours of individual nodes. For example, a test case may disable an MQTT node at the start of a flow, and tell the runtime to inject a message in its place. For each subsequent node in the flow, the test case would then be able to either to bypass it (to avoid interacting with external systems), or to add checks on what the node outputs. Each test case would then define some criteria for what it means to pass the test.</p>
<p>As with the Debugger plugin, the Test Runner would be disabled by default - so the 'production' flows aren't modified in anyway. When the Test Runner is enabled, it would then take care of running each test in return and reporting back the results.</p>
<p>The main challenge is providing a user experience that makes it easy to create these tests in a way that is consistent with the low-code nature of Node-RED.</p>
<p>Whilst this is very much a future roadmap item for Node-RED, it is one I hope we can start moving forward soon. Having a good, repeatable, testing strategy in Node-RED will make it stand-out from many of the other low-code tools and platforms available today.</p>
<h3 id="low-code-vs-lines-of-code" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#low-code-vs-lines-of-code"></a> Low-Code vs Lines-of-Code</h3>
<p>The low-code nature of Node-RED means it is easily accessible to a wide range of users. You don't need to be a seasoned software engineer to get started.</p>
<p>If you have a task to solve, and understand it well enough to break it into the right set of steps, translating that into a Node-RED flow can be much easier than having to write all of the corresponding code from scratch.</p>
<p>Just because you aren't writing code in Node-RED, it doesn't mean you shouldn't be able to benefit from ways of working that are proven to improve the end result - whilst keeping true to the low-code nature of the project.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/software-development-in-node-red/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>Both <code>nrlint</code> and Node-RED Debugger are already pre-installed in all <a href="https://app.flowfuse.com/">FlowFuse Cloud</a> hosted instances. You can start using them today via the sidebar menu.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-sidebar---eVf1YJ7O-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-sidebar---eVf1YJ7O-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-sidebar---eVf1YJ7O-650.jpeg" width="650" height="648" /></picture></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/Node-RED: The perfect adapter and middleware for your UNSHow Node-RED Enhances Connectivity and Efficiency in Unified Namespace Environments.2024-02-06T00:00:00ZZJ van de Weg<p>Digitalization is at the inflection point where it’s been adopted enough that the additional investments provided better and better ROIs for organizations. The next bump in ROI will be achieved through the UNS. A torrent of information is more useful when structured and adapted for new use-cases. Either a <a href="https://flowfuse.com/blueprints/manufacturing/performance-overview/">performance dashboard</a>, Artificial Intelligence, or station metrics – each is built faster when the data is readily available and well structured? As a company started around Node-RED, we’ve not spoken a lot where FlowFuse fits into the picture, which is what this post is about.</p>
<!--more-->
<h2 id="adapting-legacy-machines-to-the-uns" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#adapting-legacy-machines-to-the-uns"></a> Adapting legacy machines to the UNS</h2>
<p>The digitalization effort in traditional industries like manufacturing, agriculture, and beyond, has additional challenges due to the high capex assets that need to join in the effort. As these assets will not be replaced, the only way is to adapt them. Adaptation will require tooling that can interact with sensor data regardless of the protocol, data format, and data structures.</p>
<p>Node-RED bridges the gap between analog and digital data acquisition by seamlessly integrating with a vast array of protocols including serial bus support, Modbus, MQTT, and OPC-UA. Its format agnostic nature allows it to handle diverse data formats, from parsing binary data, to JSON, Protobuf (Sparkplug B), making it a versatile tool for extracting and manipulating data from various sources. With its widespread adoption, Node-RED ensures compatibility with almost every protocol, enabling users to connect and process data from a wide range of devices and applications.</p>
<h2 id="contextualisation-of-the-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#contextualisation-of-the-data"></a> Contextualisation of the data</h2>
<p>When data is captured and parsed, it needs to be contextualized. For example; in a UNS the topic hierarchy on which to publish and subscribe to is based on location – that is; context. Furthermore, a raw sensor reading might miss details like the unit of measurement, what message version is required, or doesn’t supply the information in a proper type. All these minor niggles are actual blocking issues for adopting all sensors to the UNS. In some cases makes and models of the sensor might influence the tolerances of readings, it’s a good idea in those cases to include that information in the message.</p>
<h2 id="filtering-and-preprocessing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#filtering-and-preprocessing"></a> Filtering and preprocessing</h2>
<p>Not all messages are created equal, which was discussed in <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/">an earlier post</a>. In two of the three examples provided in that post, Node-RED can preprocess or filter information before sending it to the UNS. In the case of big files or binary data, Node-RED can store it in S3 or a network attached storage layer, or even store it locally through a REST interface. The distribution of the event that created the binary data is still published through the UNS though.</p>
<p>Filtering of data is also a great use-case for Node-RED, generally just a <code>change</code> node and the data is ready to be published. The flexibility to do virtually anything with captured data is what makes Node-RED such a strong partner for your UNS.</p>
<h2 id="continuous-improvement" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#continuous-improvement"></a> Continuous improvement</h2>
<p>While it would be ideal if data schemas were stable, changes are frequent and unpredictable. It’s a non-obvious requirement for your UNS edge to be adaptable. Message structures can change, to add or remove data from them. Though also the format, from JSON to Sparkplug B, or maybe to XML. Not to say that standardization of messages will continuously require updates to leverage the UNS for higher business value.</p>
<p>A swiss-army knife as both data sender and receiver is not just a nice-to-have, it’s a requirement.</p>
<h2 id="scalable-operations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#scalable-operations"></a> Scalable operations</h2>
<p>While there are other tools available that can adapt to a few protocols, or parse a handful of data formats, there’s no alternative for Node-REDs breadth and depth of integration level. This is why many organizations have already adopted Node-RED for their edge cases, which their current standard solution doesn’t handle.</p>
<p>There’s no situation where a vendor provided, off-the-shelf solution handles protocols and formats across vendors, modern and legacy OT, that also satisfies the IT requirements unless the extensibility is handled through an Open-Source community, with compliance and security controls from a professional entity surrounding the open source project.</p>
<h3 id="how-flowfuse-enhances-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/node-red-perfect-adapter-middleware-uns/#how-flowfuse-enhances-node-red"></a> How FlowFuse Enhances Node-RED</h3>
<p>While Node-RED is powerful for implementing UNS, its management and deployment can be complex. FlowFuse simplifies this process with a unified platform that offers one-click deployment, secure management, and scalability for Node-RED applications. It enhances collaboration through centralized management of all Node-RED instances, ensuring streamlined operations and increased efficiency.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and explore how FlowFuse can transform your Node-RED experience.</strong></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/Should You Invest in Professional Services for Your Node-RED Development?Professional Services for Node-RED, When and Why?2024-02-05T00:00:00Z<p>Professional services come in many different forms. Anything from setup and configuration, flow development, to node development. In this article, we will cover a few examples of how you can leverage professional services for your Node-RED applications.</p>
<!--more-->
<h2 id="should-you-invest-in-professional-services-for-your-node-red-development%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#should-you-invest-in-professional-services-for-your-node-red-development%3F"></a> Should You Invest in Professional Services for Your Node-RED Development?</h2>
<p>Node-RED is multifaceted, and Professional Services (PS) can apply to many use cases. We will break it down into three categories that will help identify whether PS (Professional Services) is right for you. Those categories will be setup, flow development, and node development. Each takes a different skill set and has different levels of complexity.</p>
<p>Before we jump into things, we need to understand the value proposition of Node-RED. Why Node-RED? Node-RED is a tool that makes Citizen Development a reality. It should be treated that way throughout the development journey regardless of Professional Services or not. Following standard procedures that minimize complexity, for example: reducing the use of function nodes when other nodes would be more ideal. Don’t overcomplicate things that don’t need to be or better said by this quote.</p>
<blockquote>
<p>Easy things should be easy, and hard things should be possible.
-Larry Wall</p>
</blockquote>
<h3 id="setup" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#setup"></a> Setup</h3>
<p>It is often recommended to use PS for the setup and installation of products,
and Node-RED and FlowFuse aren't different. This becomes especially true if you
desire to professionalize your Node-RED instance. The reason for this is that PS
teams focus on minimizing the risk of application failure, security compromise,
and data/ip loss. This is an area of PS that can easily be overlooked, mainly
there aren't any negative consequences in the short term. The work these teams focus on prevents catastrophic issues in the future. With Node-RED and FlowFuse, these teams will focus on installing FlowFuse based on system requirements and prerequisites, migrating existing Node-RED instances, securing your environment, and integrating with your existing ecosystem of applications.</p>
<h3 id="flow-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#flow-development"></a> Flow development</h3>
<p>Oftentimes, many think of Flow development when they think of PS for Node-RED. There will be a process flow that needs to be accomplished, but it is leveraging standard nodes built within the Node-RED platform. Occasionally, there will be a need to install a custom node from the pallet manager, but the complexities in the development are adhering to a process and or visualization. There may be complex data translation, but for the most part, access to the command line of the Node-RED instance is unnecessary. Professional services may be used in situations like this where knowledge of specific systems that Node-RED is integrating into is complex. For example, a complex backend database for specific industry-wide used applications.</p>
<h3 id="node-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#node-development"></a> Node Development</h3>
<p>At the core of some integration projects lies a critical requirement: the need for specific integrations that align precisely with your application's demands. This is where Node-RED's open-source nature becomes its most significant asset. Node-RED allows for the development of custom nodes, catering to specialized protocols unique to your organization or to serve niche applications. Such node development isn't just another feature; it represents the foundational element of Node-RED, embodying the platform's core value proposition.</p>
<p>Whether addressing the need for custom integration with internal systems or expanding functionality to include less common applications, node development stands as the pivotal mechanism for enabling these capabilities. This critical development can be undertaken in-house, leveraging your team's expertise, or through PS specialized in node development.</p>
<h4 id="conflating-flow-and-node-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#conflating-flow-and-node-development"></a> Conflating Flow and Node Development</h4>
<p>It is important when scoping out a project to identify all of the potential nodes needed and identify any potential missing ones. The skills for developing Nodes and Flow development, while similar, are often different skill sets. Having these identified will help prevent scope creep and setting expectations on timelines.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/02/professional-services-for-node-red/#conclusion"></a> Conclusion</h2>
<p>As we've explored throughout this article, Professional Services for Node-RED can significantly enhance the efficiency, security, and scalability of your automation and integration projects. From the initial setup and configuration to the intricacies of flow and node development, the expertise PS teams offer can be invaluable. The decision to engage with Professional Services should be informed by your project's specific needs, internal capabilities, and strategic priorities. Whether it's leveraging PS for the foundational setup of your Node-RED instance, outsourcing flow development to expedite project timelines, or seeking expert assistance for node development, the goal is the same: create a coding environment that is conducive to citizen development.</p>
<p>If you are interested in professional services or consultation, <a href="https://flowfuse.com/professional-services/">please reach out</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/Unified Namespace: When to Use It, and When to Choose Something ElseData isn't created equal, some data doesn't fit the UNS2024-01-31T00:00:00ZZJ van de Weg<p>At FlowFuse, we're convinced of the Unified Namespace (UNS) architecture for IoT cases. It's a powerful tool that can make information much more readily available and easy to consume. However, as with any architecture, there are times when it's not the best choice. In this blog post, we'll discuss when to use the UNS and when to consider other options.</p>
<!--more-->
<h3 id="latency-sensitivity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/#latency-sensitivity"></a> Latency sensitivity</h3>
<p>When automating strictly digital tasks, there's generally no or very lenient requirements on latency. Requesting APIs from another server, normalizing data, and sending it towards another service takes very little time, though it hardly matters if you’re 100ms later than usual. In industrial automation or other cases where physical safety is safeguarded, there’s a requirement for low latency to ensure that data is transmitted and processed quickly enough to maintain real-time control of processes.</p>
<p>Extending this beyond the UNS – For controlled use cases, use specialized software and do not rely on third parties to broker the information correctly.</p>
<p>However, while control was used as an example here, the general case here is not the UNS for real-time, low-latency communication.</p>
<h3 id="large-files-or-binary-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/#large-files-or-binary-data"></a> Large files or binary data</h3>
<p>Sending large blobs of binary data, such as pictures and archives, through UNS is not recommended due to several drawbacks. First, UNS is optimized for small, mostly text-based communication, and sending binary data through it can significantly increase message size and processing overhead. As the sender of the data, there’s also generally no control over the number of receivers of the data, so if you send a large file to the broker once, it might need to be copied many times for every receiver.</p>
<p>To overcome these limitations, it is more efficient and practical to store binary data elsewhere, such as on a shared storage location or a cloud service. Once the data is stored externally, a reference to its location can be sent through UNS.</p>
<h3 id="data-security-and-data-access" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/#data-security-and-data-access"></a> Data security and data access</h3>
<p>The UNS becomes more useful the more hubs are connected, and it stands to reason that better business outcomes are achieved when everything is connected. Not just the sensor data (Level 1 of the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#automation-pyramid---visualization">automation pyramid</a> <em>except actuators</em>), but also the customer-facing systems like your ERP (Level 4 or the automation pyramid), and even your CRM, Customer Relationship Management. This would allow better insight and communication with the customer when, for example, their car is done with production and will be shipped to them. The CRM contains personal details about the customer. Publishing what a customer ordered might thus disclose personally identifiable information (PII).</p>
<p>Connecting a CRM is certainly possible, and could streamline the supply chain, but PII shouldn’t be published. Consider if there’s a unique ID for the customer instead to use. Does your CRM, for example, keep a customer ID? If not, mask the information instead. Use strong hashing algorithms like SHA-512 to hash their email addresses. This way, other systems can cross reference messages received in the UNS, without ever knowing the customer PII. Another venue to explore is topic-based authentication for data receivers, which most data brokers provide. However, stripping and masking PII is still highly recommended either way.</p>
<h3 id="in-conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-when-not-to-use/#in-conclusion"></a> In Conclusion</h3>
<p>The Unified Namespace is a powerful architecture that can make IoT data more accessible and easier to consume. However, it's not always the best choice for all applications. When considering whether to use the UNS, be sure to weigh the benefits against the potential risks. If you need low latency, strong data security, or fine-grained control over data access, you may need to adjust your architecture pattern or instantiate point-to-point connections through REST or other interfaces.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/AI and ChatGPT - Revolutionizing the Manufacturing IndustryHow AI and Conversational Technologies are Transforming Industrial Processes2024-01-31T00:00:00Z<p>The application of artificial intelligence (AI) in various industries, particularly in manufacturing, is a topic of growing interest. The evolution of technologies like ChatGPT is driving significant changes in this sector. For a more nuanced understanding, we reference four informative blog posts from our team members. The first post, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/">"AI Use Cases that are shaping the next manufacturing frontier"</a>, offers an insightful overview of AI's role in diverse areas. Following this, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/">"ChatGPT AI Assistants with Node-RED"</a> examines the specific impact of AI assistants. The third article, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/chatgpt-gpt/">"Node-RED Builder a ChatGPT GPT"</a>, discusses the capabilities of generative pre-trained transformers like ChatGPT. Lastly, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/">"How ChatGPT improves Node-RED Developer Experience"</a> explores ChatGPT's application in Node-RED development, an important aspect for many in manufacturing.</p>
<!--more-->
<h2 id="how-ai-and-chatgpt-are-impacting-manufacturing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#how-ai-and-chatgpt-are-impacting-manufacturing"></a> How AI and ChatGPT are Impacting Manufacturing</h2>
<p>AI and ChatGPT's integration into manufacturing indicates a shift in production process management. Here are some key impacts:</p>
<h3 id="boosting-efficiency-and-productivity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#boosting-efficiency-and-productivity"></a> Boosting Efficiency and Productivity</h3>
<p>AI, especially ChatGPT, enhances manufacturing efficiency by analyzing data to improve production lines, predict maintenance, and aid in design and development.</p>
<h3 id="automating-routine-tasks" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#automating-routine-tasks"></a> Automating Routine Tasks</h3>
<p>AI is adept at handling repetitive tasks, speeding up manufacturing and allowing human workers to engage in more complex production aspects.</p>
<h3 id="elevating-quality-control" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#elevating-quality-control"></a> Elevating Quality Control</h3>
<p>AI algorithms consistently ensure high-quality standards, quickly identifying and fixing product defects or deviations.</p>
<h3 id="enabling-customization-and-flexibility" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#enabling-customization-and-flexibility"></a> Enabling Customization and Flexibility</h3>
<p>AI's learning and adaptability make it suitable for customizing production, allowing manufacturers to meet specific customer demands more efficiently.</p>
<h3 id="transforming-the-workforce" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#transforming-the-workforce"></a> Transforming the Workforce</h3>
<p>AI in manufacturing necessitates skilled workers to operate and maintain these systems, altering job roles and responsibilities.</p>
<h2 id="pros-and-cons-of-ai-and-chatgpt-in-manufacturing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#pros-and-cons-of-ai-and-chatgpt-in-manufacturing"></a> Pros and Cons of AI and ChatGPT in Manufacturing</h2>
<h3 id="pros" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#pros"></a> Pros</h3>
<ol>
<li>Boosted Productivity: AI-driven automation increases production efficiency.</li>
<li>Consistent Quality Assurance: AI ensures ongoing product quality.</li>
<li>Reduced Costs: AI optimizes resource use and minimizes waste.</li>
<li>Encouraging Innovation: AI facilitates new manufacturing methods and products.</li>
<li>Enhancing Safety: AI reduces human exposure to hazardous manufacturing conditions.</li>
</ol>
<h3 id="cons" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#cons"></a> Cons</h3>
<ol>
<li>Initial Investment Costs: AI technology implementation can be expensive.</li>
<li>Need for Skilled Labor: Demand for workers proficient in AI technologies is growing.</li>
<li>Job Role Changes: Automation might decrease the need for certain labor roles.</li>
<li>Security Concerns: AI systems can be susceptible to cyber threats.</li>
<li>Technological Dependence: Excessive reliance on AI could limit problem-solving abilities in workers.</li>
</ol>
<h2 id="frequently-asked-questions-(faqs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/revolutionizing-manufacturing-impact-ai-chatgpt-technologies/#frequently-asked-questions-(faqs)"></a> Frequently Asked Questions (FAQs)</h2>
<details>
<summary>1. How is AI changing manufacturing?</summary>
<br />
<strong>AI is altering manufacturing through automation, optimizing efficiency, and fostering production innovations.</strong>
</details>
<details>
<summary>2. What is ChatGPT's role in manufacturing?</summary>
<br />
<strong>ChatGPT aids in data analysis, automates processes, and improves communication and documentation in manufacturing.</strong>
</details>
<details>
<summary>3. Are manufacturing jobs at risk due to AI?</summary>
<br />
<strong>While AI may automate some repetitive jobs, it also creates opportunities for skilled labor in technology management and development.</strong>
</details>
<details>
<summary>4. Can AI enhance manufacturing product quality?</summary>
<br />
<strong>Yes, AI's continuous monitoring and analysis significantly boost quality control.</strong>
</details>
<details>
<summary>5. What are the main challenges of integrating AI in manufacturing?</summary>
<br />
<strong>Challenges include high implementation costs, the need for skilled labor, and transitioning to automated processes.</strong>
</details>
<details>
<summary>6. Is AI cost-effective in manufacturing?</summary>
<br />
<strong>Despite high initial costs, AI can lead to long-term savings through improved efficiency and waste reduction.</strong>
</details>
<details>
<summary>7. How does AI affect manufacturing worker safety?</summary>
<br />
<strong>AI reduces risk by taking over hazardous tasks, improving overall workplace safety.</strong>
</details>
<details>
<summary>8. Can small manufacturers benefit from AI?</summary>
<br />
<strong>Yes, AI solutions are increasingly accessible for small-scale manufacturers.</strong>
</details>
<details>
<summary>9. What training is required for AI-enabled manufacturing workers?</summary>
<br />
<strong>Training in AI system operation, data analysis, and potentially programming skills is needed.</strong>
</details>
<details>
<summary>10. What does the future hold for AI in manufacturing?</summary>
<br />
<strong>The future suggests more integrated, intelligent, and adaptable manufacturing processes driven by AI advancements.</strong>
</details>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/Step-by-Step Guide to Deploying Node-RED with FlowFuse in balenaCloudFleet management made easier with FlowFuse and balena.2024-01-30T00:00:00Z<p>In a <a href="https://flowfuse.com/webinars/2024/balena/">recent webinar with balena</a>, we explored the dynamic capabilities of deploying FlowFuse to a fleet of devices using <a href="https://www.balena.io/cloud">balenaCloud</a>. This blog post serves as a practical guide to replicate that process, specifically tailored for those aiming to streamline their deployment of FlowFuse in an efficient and user-friendly manner.</p>
<!--more-->
<h2 id="how-to-implement-flowfuse-with-balenacloud-on-a-fleet-of-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#how-to-implement-flowfuse-with-balenacloud-on-a-fleet-of-devices"></a> How to Implement FlowFuse with balenaCloud on a Fleet of devices</h2>
<p><a href="https://www.youtube.com/watch?v=cKFu1ljUlKE" title="Deploying Node-RED with FlowFuse in balenaCloud"><img alt="Deploying Node-RED with FlowFuse in balenaCloud" loading="lazy" decoding="async" src="https://i.ytimg.com/vi/cKFu1ljUlKE/hqdefault.jpg" width="undefined" height="undefined" /></a></p>
<h3 id="preparation-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#preparation-steps"></a> Preparation Steps</h3>
<p>Before diving into the deployment process, it's crucial to familiarize yourself with key resources. We recommend reviewing our previous <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/">blog post</a> on deploying the FlowFuse Device Agent via balena. This post contains a vital link to the GitHub repository, essential for deploying FlowFuse with balena, laying the groundwork for the steps ahead.</p>
<h3 id="creating-a-new-fleet-in-balenacloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#creating-a-new-fleet-in-balenacloud"></a> Creating a New Fleet in balenaCloud</h3>
<ol>
<li>Navigate to the <a href="https://github.com/FlowFuse/balena-device-agent">FlowFuse git</a> repository. Click on the <strong>Deploy with balena</strong> button.</li>
<li>Name your fleet.</li>
<li>Select your default device.</li>
<li>Click <strong>Create and Deploy</strong>.</li>
</ol>
<h3 id="adding-devices-to-the-fleet" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#adding-devices-to-the-fleet"></a> Adding Devices to the Fleet</h3>
<p>Once your fleet is created, the next step is to add devices. To add a device to your fleet, follow these <a href="https://docs.balena.io/learn/getting-started/var-som-mx6/rust/#add-a-device-and-download-os">instructions</a>.</p>
<h3 id="setting-up-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#setting-up-flowfuse"></a> Setting Up FlowFuse</h3>
<p>Setting up FlowFuse correctly is essential for seamless operation:</p>
<ol>
<li>Create a new instance within FlowFuse or use an existing one if you prefer. Follow these <a href="https://flowfuse.com/docs/user/introduction/#creating-a-node-red-instance">instructions</a> to create a new instance.</li>
<li>Create a <strong>Device Provisioning Token</strong> by following these <a href="https://flowfuse.com/docs/device-agent/register/#bulk-registration">instructions</a>.</li>
<li>Ensure you add the FlowFuse Node-RED application you want the devices to provision. If left at default, devices will need to be manually added to applications.</li>
</ol>
<h3 id="using-the-device-provisioning-token" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#using-the-device-provisioning-token"></a> Using the Device Provisioning Token</h3>
<ol>
<li>First, convert the contents of the Device Provisioning Token to base64. Follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#environment-variable">instructions</a> to convert the file to base64.</li>
<li>Once converted, import this string into balena as a <strong>Fleet</strong> level variable, not a device level variable. Follow these <a href="https://docs.balena.io/learn/manage/variables/#fleet-wide-variables">instructions</a> to import the Fleet level variable with the Name <code>FF_DEVICE_YML</code>.</li>
<li>This action will provision any new device added to the fleet with the yaml file configuration, automatically adding the device to a FlowFuse instance.</li>
</ol>
<h3 id="deploying-and-testing-the-flowfuse-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#deploying-and-testing-the-flowfuse-instance"></a> Deploying and Testing the FlowFuse Instance</h3>
<p>Deploying the FlowFuse instance brings everything together:</p>
<ol>
<li>Navigate to your FlowFuse application created earlier.</li>
<li>Go to your devices and you should now see your newly provisioned devices from balena.</li>
<li>If this is your first time setting up your fleet, the device will not have a snapshot. You will need to deploy a snapshot. Follow these <a href="https://flowfuse.com/docs/user/snapshots/#create-a-snapshot">instructions</a> to do so. Ensure that you select <strong>Set Target Snapshot</strong>.</li>
<li>Once complete, the FlowFuse instance will deploy to your device(s).</li>
</ol>
<h2 id="integrating-influxdb-(optional)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#integrating-influxdb-(optional)"></a> Integrating InfluxDB (Optional)</h2>
<p>Integrating InfluxDB enables effective data storage and management:</p>
<ol>
<li>Similar to the previous steps, navigate to this <a href="https://github.com/mpous/flowfuse-agent-influx-balena/tree/main?tab=readme-ov-file">Github repository</a> and click <strong>Deploy with balena</strong>.</li>
<li>This time, instead of creating a new fleet, select <strong>Use an existing fleet instead</strong>.</li>
<li>Choose your fleet for deployment and select <strong>Deploy to fleet</strong>.</li>
</ol>
<h3 id="data-generation-and-management" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#data-generation-and-management"></a> Data Generation and Management</h3>
<p>For testing, we have created a flow to get you started. Follow this <a href="https://flows.nodered.org/flow/66f37bb739b6cdb0c7ad3a4e2edd68ef">link</a> and import it. There are four sets of flows for you to begin with. The first is for data generation. The second is a manual data generation flow. The third is key as it initiates the creation of a database, in this case, <strong>mydb</strong>. The last flow is a simple query that pulls data from InfluxDB.</p>
<ol>
<li>Import the flows into your FlowFuse instance of Node-RED and deploy. Follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#2.-import-helpful-example-flows-provided-with-custom-nodes">instructions</a> for importing and exporting.</li>
<li>Return to Flowfuse, go to your instance, and create another <a href="https://flowfuse.com/docs/user/snapshots/#create-a-snapshot">snapshot</a>.</li>
<li>Ensure that you <strong>Set Target Snapshot</strong>.</li>
</ol>
<h3 id="finalizing-and-testing-the-setup" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#finalizing-and-testing-the-setup"></a> Finalizing and Testing the Setup</h3>
<p>The final steps ensure that your setup is fully operational:</p>
<ol>
<li>Once deployed, navigate to the device.</li>
<li><a href="https://flowfuse.com/docs/device-agent/deploy/#editing-the-node-red-flows-on-a-remote-instance-that-is-assigned-to-an-application">Enable Developer Mode</a>.</li>
<li>Next, click the newly revealed button, <strong>Open Editor</strong>, to access the deployed Flow.</li>
</ol>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/how-to-deploy-node-red-with-flowfuse-to-balenacloud/#conclusion"></a> Conclusion</h3>
<p>Implementing FlowFuse with balenaCloud significantly enhances your device management and data processing capabilities. This guide provides a foundational approach, but don't hesitate to delve deeper into each step to tailor the setup to your specific needs.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/Speech-Driven Chatbot System with Node-REDGuide to building speech-driven chatbot using Node-RED, speech recognition, and Dashboard 2.0.2024-01-29T00:00:00ZSumit Shinde<p>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.</p>
<p>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.
Let's get started!</p>
<!--more-->
<h2 id="what-exactly-is-speech-recognition-and-synthesis%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#what-exactly-is-speech-recognition-and-synthesis%3F"></a> What exactly is speech recognition and synthesis?</h2>
<p>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.</p>
<ul>
<li><strong>Hands-Free Operation:</strong> 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.</li>
<li><strong>Accessibility:</strong> It allows individuals with visual impairments to access digital content through spoken words and as discussed above, to control devices without physical interaction.</li>
<li><strong>Efficient Content Consumption:</strong> 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.</li>
</ul>
<h2 id="installing-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#installing-dashboard-2.0"></a> Installing Dashboard 2.0</h2>
<p>Install Dashboard 2.0. Follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">instructions</a> to get up and running.</p>
<h2 id="building-speech-to-text-vue-component" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#building-speech-to-text-vue-component"></a> Building Speech-to-Text Vue component</h2>
<p>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 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API">Web Speech API</a> is not an external API.
This 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.</p>
<ol>
<li>Drag a ui template widget to canvas and select the created group.</li>
<li>Paste the below Vue snippets into the template widget step by step.</li>
</ol>
<p>If you are unfamiliar with Vue, we have added comments that will help you to understand the code better.</p>
<p>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.</p>
<div style="position: relative" id="code-container-60">
<pre class="language-html"><code id="code-60" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Button triggers recording when clicked --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">@click</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>startRecording<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Microphone icon inside the button --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microphone<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">height</span><span class="token punctuation">:</span> 62px<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> 62px</span><span class="token punctuation">"</span></span></span> <span class="token attr-name">:src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>microphoneIcon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Displaying speech recognition results --></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>strong</span><span class="token punctuation">></span></span>You:<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>strong</span><span class="token punctuation">></span></span> {{ results }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-60" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Now paste the below script right after the HTML in the template widget, This script adds functionality of speech recognition in our system.</p>
<div style="position: relative" id="code-container-64">
<pre class="language-html"><code id="code-64" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Initial data properties</span><br /> <span class="token literal-property property">buttonText</span><span class="token operator">:</span> <span class="token string">'Speak'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">microphoneIcon</span><span class="token operator">:</span> <span class="token string">'http://icons.iconarchive.com/icons/blackvariant/button-ui-requests-15/512/Microphone-icon.png'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">recognition</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">results</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Method to start recording</span><br /> <span class="token function">startRecording</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>buttonText <span class="token operator">=</span> <span class="token string">'Recording'</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Method to process the speech recognition results</span><br /> <span class="token function">processSpeech</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">let</span> results <span class="token operator">=</span> Array<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>results<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">result</span> <span class="token operator">=></span> result<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>transcript<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>results <span class="token operator">=</span> results<span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">$emit</span><span class="token punctuation">(</span><span class="token string">'speak'</span><span class="token punctuation">,</span> results<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">//Sending result to next node as payload</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>results<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Method to handle the start of recognition</span><br /> <span class="token function">handleRecognitionStart</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>microphoneIcon <span class="token operator">=</span> <span class="token string">'https://upload.wikimedia.org/wikipedia/commons/0/06/Mic-Animation.gif'</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Method to handle recognition errors</span><br /> <span class="token function">handleRecognitionError</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>microphoneIcon <span class="token operator">=</span> event<span class="token punctuation">.</span>error <span class="token operator">===</span> <span class="token string">'no-speech'</span> <span class="token operator">||</span> event<span class="token punctuation">.</span>error <span class="token operator">===</span> <span class="token string">'audio-capture'</span><br /> <span class="token operator">?</span> <span class="token string">'https://i.ytimg.com/vi/9YQU797Oy0Y/hqdefault.jpg'</span><br /> <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>microphoneIcon<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Method to handle the end of recognition</span><br /> <span class="token function">handleRecognitionEnd</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>microphoneIcon <span class="token operator">=</span> <span class="token string">'http://icons.iconarchive.com/icons/blackvariant/button-ui-requests-15/512/Microphone-icon.png'</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token comment">// Method to set up the SpeechRecognition object</span><br /> <span class="token function">setupRecognition</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">webkitSpeechRecognition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>continuous <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>interimResults <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>onresult <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>processSpeech<span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>onstart <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleRecognitionStart<span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>onerror <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleRecognitionError<span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>recognition<span class="token punctuation">.</span>onend <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>handleRecognitionEnd<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Initialize SpeechRecognition when the component is mounted</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setupRecognition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-64" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="adding-an-environment-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#adding-an-environment-variable"></a> Adding an Environment variable</h2>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Using Environment Variables in Node-RED</a></p>
<ol>
<li>Navigate to the instance's setting and then go to the environment section.</li>
<li>Click on the <code>add variable</code> button and add a variable for Chat-gpt API.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-650.avif 650w, https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-650.webp 650w, https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-650.jpeg 650w, https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Setting environment variable for chat-gpt token" alt=""Setting environment variable for Chat-gpt token"" loading="lazy" decoding="async" src="https://flowfuse.com/img/speech-driven-chatbot-environment-section-byp_bdQ1f_-650.jpeg" width="1300" height="641" /></picture></p>
<h2 id="setting-msg-property" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#setting-msg-property"></a> Setting msg property</h2>
<p>Now let’s set that added environment variables as msg's property.</p>
<ol>
<li>Add a change node to canvas.</li>
<li>Set environment variable to ms.token property.</li>
<li>Connect the change node’s input to the template widget’s output.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/speech-driven-chatbot-change-node-Yxl1FrDmYv-616.avif 616w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/speech-driven-chatbot-change-node-Yxl1FrDmYv-616.webp 616w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Setting msg's property for Chat-gpt token" alt=""Setting msg's property for Chat-gpt token"" loading="lazy" decoding="async" src="https://flowfuse.com/img/speech-driven-chatbot-change-node-Yxl1FrDmYv-616.jpeg" width="616" height="462" /></picture></p>
<h2 id="installing-and-configuring-custom-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#installing-and-configuring-custom-node"></a> Installing and configuring custom node</h2>
<p>In this section, we will install a custom node that will allow us to interact with the Chat-gpt AI model.</p>
<ol>
<li>Install <code>@sumit_shinde_84/node-red-contrib-node-gpt</code> by pallet manager, you can use other nodes according to your preference.</li>
<li>Drag a ChatGPT node to canvas.</li>
<li>Connect the ChatGPT node’s input to the change node’s output.</li>
</ol>
<h2 id="building-text-to-speech-vue-component" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#building-text-to-speech-vue-component"></a> Building Text-to-Speech Vue component</h2>
<p>We will build a Vue component that converts text received from ChatGPT into speech.</p>
<ol>
<li>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.</li>
<li>Paste the below Vue snippets into the template widget.</li>
<li>Connect the template widget’s input to the ChatGPT node’s output.</li>
</ol>
<p>Paste the below snippet in the template widget which displays chat-gpt response on the dashboard</p>
<div style="position: relative" id="code-container-161">
<pre class="language-html"><code id="code-161" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>strong</span><span class="token punctuation">></span></span> Chat-gpt: <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>strong</span><span class="token punctuation">></span></span> {{textToSpeech}}<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-161" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>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.</p>
<div style="position: relative" id="code-container-165">
<pre class="language-html"><code id="code-165" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Data property to store the text to be spoken</span><br /> <span class="token literal-property property">textToSpeech</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Method to trigger text-to-speech</span><br /> <span class="token function">speakText</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Check if there is non-empty text to speech</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>textToSpeech<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token string">''</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Create a SpeechSynthesisUtterance with the text to be spoken</span><br /> <span class="token keyword">const</span> utterance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SpeechSynthesisUtterance</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>textToSpeech<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token comment">// Use the SpeechSynthesis API to speak the provided text</span><br /> window<span class="token punctuation">.</span>speechSynthesis<span class="token punctuation">.</span><span class="token function">speak</span><span class="token punctuation">(</span>utterance<span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Event listener for receiving messages </span><br /> <br /> <span class="token keyword">this</span><span class="token punctuation">.</span>$socket<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'msg-input:'</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">msg</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /><br /> <span class="token comment">// Update the textToSpeech property with the received message payload</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>textToSpeech <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">;</span><br /> <br /> <span class="token comment">// Trigger text-to-speech with the received message</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">speakText</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /><span class="token punctuation">}</span><span class="token punctuation">;</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span> <span class="token attr-name">scoped</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /> <span class="token selector">textarea</span> <span class="token punctuation">{</span><br /> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br /> <span class="token property">height</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br /> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-165" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Your final flow should look like this:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/speech-driven-chatbot-flow-dZRhFkXA0B-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/speech-driven-chatbot-flow-dZRhFkXA0B-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Speech Driven Chatbot system flow" alt=""Speech Driven Chatbot system flow"" loading="lazy" decoding="async" src="https://flowfuse.com/img/speech-driven-chatbot-flow-dZRhFkXA0B-650.jpeg" width="650" height="135" /></picture></p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#deploying-the-flow"></a> Deploying the Flow</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-650.avif 650w, https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-650.webp 650w, https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-650.jpeg 650w, https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Deploying Sentiment analysis Node-RED flow" alt=""Deploying Sentiment analysis Node-RED flow"" loading="lazy" decoding="async" src="https://flowfuse.com/img/speech-driven-chatbot-flowfue-editor-lsVJ-mg72b-650.jpeg" width="1300" height="615" /></picture></p>
<p>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 <code>https://<your-instance-name>.flowfuse.cloud/dashboard</code></p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/speech-driven-chatbot-system-8ga4czQg42-648.gif 648w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Speech Driven Chatbot using Node-RED Dashboard 2.0" alt=""Speech Driven Chatbot using Node-RED Dashboard 2.0"" loading="lazy" decoding="async" src="https://flowfuse.com/img/speech-driven-chatbot-system-8ga4czQg42-648.webp" width="648" height="400" /></picture></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/speech-driven-chatbot-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/Personalised Multi-user Dashboards with Node-RED Dashboard 2.0!Explore how to build multi-user Dashboards, secured with FlowFuse Cloud!2024-01-24T00:00:00ZJoe Pavitt<p>This week has seen the release of the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-ga/">first major version of Node-RED Dashboard 2.0</a>, with it, we've made available a new FlowFuse-exclusive feature, personalised multi-user dashboards.</p>
<p>This new feature will allow you to build applications that provide unique data to each user, build admin-only views, and track user activity, to name but a few. We're really excited to see what the Node-RED Community and our FlowFuse customers can do with such a powerful and flexible framework.</p>
<!--more-->
<h2 id="personalised-multi-user-dashboards" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#personalised-multi-user-dashboards"></a> Personalised Multi User Dashboards</h2>
<p>The original Node-RED Dashboard was built with a "single source of truth", no matter how many users interacted with the dashboard, each user would always see the same data. This is great for prototyping, or hobby projects, but as you scale up your Node-RED usage, you'll want to be able to have unique dashboard experiences for each user.</p>
<h3 id="getting-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#getting-started"></a> Getting Started</h3>
<p>To enable personalised multi-user dashboards, you'll need to be using FlowFuse, and complete two steps:</p>
<h4 id="step-1%3A-enable-%22flowfuse-user-authentication%22" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#step-1%3A-enable-%22flowfuse-user-authentication%22"></a> Step 1: Enable "FlowFuse User Authentication"</h4>
<p>All instances on FlowFuse can be configured with <em>"FlowFuse User Authentication"</em> in the "Security" Settings. This option requires any user that wants access to your Editor or dashboard to be authorized by FlowFuse first.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-ffauth-0hagBkY4IT-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-ffauth-0hagBkY4IT-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Screenshot of the 'Security' settings available for any Instances running in FlowFuse" alt=""Screenshot of the 'Security' settings available for any Instances running in FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-ffauth-0hagBkY4IT-1920.jpeg" width="1920" height="1101" /></picture></p>
<figcaption class="-mt-6 mb-4 text-center"><b>"Screenshot of the 'Security' settings available for any Instances running in FlowFuse"</b></figcaption>
<h4 id="step-2%3A-install-flowfuse's-user-addon" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#step-2%3A-install-flowfuse's-user-addon"></a> Step 2: Install FlowFuse's User Addon</h4>
<h5 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#flowfuse-cloud"></a> FlowFuse Cloud</h5>
<p><em>Note: Every instance created from today onwards automatically comes with the necessary configuration. Already created instances need to be manually restarted.</em></p>
<p>The Personalised Multi-User Dashboard plugin, <code>@flowfuse/node-red-dashboard-2-user-addon</code>, is available in our <a href="https://flowfuse.com/certified-nodes/">Certified Nodes</a> catalogue, accessible to our Teams and Enterprise customers.</p>
<p>Once the "FlowFuse User Authentication" option has been enabled on your instance, you can then install our plugin, <code>@flowfuse/node-red-dashboard-2-user-addon</code>, through the "Manage Palette" option in the Node-RED Editor.</p>
<p>For your devices, we provide the necessary configuration and access token upon request, so that your Node-RED devices can also benefit from a Personalised Multi-user Dashboard.</p>
<h5 id="flowfuse-self-hosted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#flowfuse-self-hosted"></a> FlowFuse Self-Hosted</h5>
<p>For all our Teams and Enterprise Self-Hosted customers who also want to use the Certified Nodes and the Multi-User Dashboard, we provide all necessary configurations upon request to get started.</p>
<p>Alternatively, if you're looking to elevate your Node-RED infrastructure, <a href="https://flowfuse.com/contact-us/">book in a chat with us</a> to talk about how FlowFuse can help.</p>
<h3 id="using-the-plugin" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#using-the-plugin"></a> Using the Plugin</h3>
<p>Once enabled, any messages emitted by a Dashboard 2.0 node will contain a new <code>msg._client.user</code> object, e.g:</p>
<div style="position: relative" id="code-container-62">
<pre class="language-js"><code id="code-62" class="language-js"><span class="token punctuation">{</span><br /> <span class="token string-property property">"userId"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token comment">// unique identifier for the user</span><br /> <span class="token string-property property">"username"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token comment">// FlowFuse Username</span><br /> <span class="token string-property property">"email"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token comment">// E-Mail Address connected to their FlowFuse account</span><br /> <span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token comment">// Full Name</span><br /> <span class="token string-property property">"image"</span><span class="token operator">:</span> <span class="token string">""</span> <span class="token comment">// User Avatar from FlowFuse</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-62" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Then, when running Node-RED Dashboard 2.0 on FlowFuse, you'll have a new sidebar option in the Node-RED Editor, which allows you to control which node types allow for "client constraints".</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-ff-settings-Jf8rG8WpVt-1100.avif 1100w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-ff-settings-Jf8rG8WpVt-1100.webp 1100w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="The new 'FF Auth' options available in Node-RED to allow for client constraints" alt="The new 'FF Auth' options available in Node-RED to allow for client constraints" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-ff-settings-Jf8rG8WpVt-1100.jpeg" width="1100" height="1316" /></picture></p>
<figcaption class="-mt-6 mb-4 text-center"><b>A screenshot of the new 'FF Auth' options available in Node-RED to allow for client constraints on different node types.</b></figcaption>
<p>In the original Node-RED Dashboard, this was <em>always</em> enabled for the <code>ui-notification</code> and <code>ui-control</code> nodes, whereby you could include <code>msg.socket</code> data and it would only then send that message to the specified client. For Dashboard 2.0 we've extended this concept so that as a Node-RED Developer, you can now include <code>msg._client.user</code> data in any message sent to a Dashboard 2.0 node. Under the covers, our FlowFuse-exclusive plugin will then automatically filter messages to only send to the relevant user's connection.</p>
<p>Utilising this feature, below you can see an example where we send data to a <code>ui-template</code> to render a custom table for each user. Under the covers this is a <code>ui-event</code> node (triggered on a page view), which then uses the <code>msg._client.user</code> object to make a REST API call to retrieve a list of todo items for that specific user. We then wire the response into the <code>ui-template</code>, which has been configured to "Accept Client Constraints", and so only sends this data to User 2's dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-admin-tasks-OhvPu-0L5n-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-admin-tasks-OhvPu-0L5n-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Showing Admin Task View" alt="Showing Admin Task View" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-admin-tasks-OhvPu-0L5n-1920.jpeg" width="1920" height="1111" /></picture></p>
<figcaption class="-mt-6 mb-4 text-center"><b>Example of a dashboard that displays user-specific content.</b></figcaption>
<p>Note too that we're also utilising the new <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#teleports">Teleport</a> option available in a <code>ui-template</code> which allows us to define content to show in the top-right of the dashboard, in this case, a little <em>"Hi {username}"</em> message.</p>
<h3 id="examples" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#examples"></a> Examples</h3>
<h4 id="rendering-logged-in-user-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#rendering-logged-in-user-data"></a> Rendering Logged In User Data</h4>
<p>In the previous example, you may have noticed that we're also displaying a welcome to the authenticated user on our dashboard, this means that we have access to the full User object within any <code>ui-template</code> that we render too.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Showing User Unique Data" alt="Showing User Unique Data" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1920.jpeg" width="1920" height="1111" /></picture></p>
<figcaption class="-mt-6 mb-4 text-center"><b>The "Admin" view that is only made available to users registered as an "admin".</b></figcaption>
<p>Under the covers, we're appending our <code>user</code> object to the <code>msg</code> object, via the SocketIO <code>auth</code> option. We make the <code>socketio</code> object available via a computed <code>setup</code> variable, this means that we can access this data in any <code>ui-template</code> node, and render like so:</p>
<div style="position: relative" id="code-container-99">
<pre class="language-html"><code id="code-99" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hi, {{ setup?.socketio?.auth?.user?.name }}!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-99" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>To enable custom user-by-user content in a <code>ui-template</code> though, we must allow it to "Accept Client Constraints". This means that if a <code>.msg._client.user</code> value is included in any messages sent to a <code>ui-template</code> node, then the underlying SocketIO message will be filtered to only send to the relevant user's connection, and no others.</p>
<h4 id="admin-only-views" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#admin-only-views"></a> Admin Only Views</h4>
<p>With this new functionality we can also now show/hide content based on the authenticated user.</p>
<p>We recently introduced the option to <a href="https://github.com/FlowFuse/node-red-dashboard/pull/484">set default visiblity & interaction states</a>. This was partly introduced because it's a good practice to set the default "Visibility" option for any admin-only pages to "Hidden", and then use a <code>ui-control</code> node to show the content only to the relevant admins.</p>
<iframe width="100%" height="100%" src="https://flows.nodered.org/flow/2fe8e6f1e7002f1ff6a9195ad1a153b6/share" allow="clipboard-read; clipboard-write" style="border: none; margin-bottom: 12px;"></iframe>
<p>Let's breakdown the above flow:</p>
<ol>
<li>We wire a <code>ui-event</code> node (which emits each time a user views a page) into a switch node</li>
<li>Our switch node checks the <code>user.username</code> against a known list of admin users and branches "admin:" and "non-admin" users</li>
<li>For admin users, the <code>change</code> node defines a message for our <code>ui-control</code> node to dynamically show content, in this case an "Admin" page, when appropriate.</li>
</ol>
<div style="position: relative" id="code-container-133">
<pre class="language-json"><code id="code-133" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"pages"</span><span class="token operator">:</span><span class="token punctuation">{</span><br /> <span class="token property">"show"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"Admin View"</span><span class="token punctuation">]</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-133" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>All events going into <code>ui-control</code> are automatically filtered based on the <code>msg._client.user</code> object, so only the Admin users will receive the message to show the "Admin View" page, resulting in:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-admin-FyPHrtXpYw-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-admin-FyPHrtXpYw-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" title="Showing Admin Only View" alt="Showing Admin Only View" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-admin-FyPHrtXpYw-1920.jpeg" width="1920" height="1111" /></picture></p>
<figcaption class="-mt-6 mb-4 text-center"><b>The "Admin" view that is only made available to users registered as an "admin".</b></figcaption>
<p>Further extensions of this could also check <code>ui-event</code> in case a non-admin user tries to access the <code>/admin</code> page directly, in which case we can utilise <code>ui-control</code> to navigate the user away from the page immediately. See the <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-control.html#navigation">ui-control documentation</a> for more details on this.</p>
<h2 id="upcoming-webinar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#upcoming-webinar"></a> Upcoming Webinar</h2>
<p>If you're interested in learning more about Dashboard 2.0 and in particular multi-user Dashboards, we're hosting a webinar on Thursday, 29th February. You can find out more information <a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">here</a></p>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/#follow-our-progress"></a> Follow our Progress</h2>
<p>We aren't stopping here, we'll continue to push Dashboard 2.0 forward with future development, and you can track that progress on our GitHub Projects:</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/5">Dashboard 1.0 Feature Parity Tracker</a></li>
</ul>
<p>If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-ga/Node-RED Dashboard 2.0 is Generally Available!This week sees our first major version release of Node-RED Dashboard 2.0!2024-01-24T00:00:00ZJoe Pavitt<p>Back in <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/">June 2023</a> we announced that FlowFuse would be investing into building out the next generation of Node-RED Dashboard, the most popular UI framework for Node-RED.
We followed this up with the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/">first release</a> (<code>0.0.1</code>) in July, just one month later, and today, we are pleased to announce that we have reached a major milestone in this journey, with the release of our first major version (<code>1.0.0</code>) of Node-RED Dashboard 2.0.</p>
<!--more-->
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-650.avif 650w, https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-650.webp 650w, https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-650.jpeg 650w, https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Dashboard 2.0 Example showing weather data" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-ga-example-GdrlGHRgNj-650.jpeg" width="1300" height="886" /></picture></p>
<p>With our <code>1.0.0</code> release, you can now build your dashboards on a reliable and stable package, and we invite you to to start contributing your own third-party <a href="https://dashboard.flowfuse.com/contributing/widgets/third-party.html">widgets</a> and <a href="https://dashboard.flowfuse.com/contributing/plugins/">plugins</a>. We're excited to see what the community can contribute and build on top of this new Dashboard 2.0 framework, and we'll be continuing development to the core collection of widgets too.</p>
<p>With Node-RED Dashboard 2.0, we have re-built the original Node-RED Dashboard from the ground up. It is now extensible due to it being VueJS-based, completely responsive down to mobile, and we've made many quality of life improvements across the board to the existing widget collection, as well as adding a few new ones too.</p>
<h2 id="what's-new-in-dashboard-2.0%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-ga/#what's-new-in-dashboard-2.0%3F"></a> What's new in Dashboard 2.0?</h2>
<p>We've shared plenty of updates since we started, detailing the feature parity with the original Node-RED Dashboard, as well as some of the new widgets and features we've added to the new Dashboard, such as Markdown, Mermaid Charts and new Layout Options, you can read more about those here:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/">Dynamic Markdown, Tables & Notebooks</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/">UI Chart Improvements</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/">Building a Custom Video Player</a></li>
</ul>
<p>Furthermore, the most requested feature for the legacy dashboard has been implemented in Dashboard 2.0, the ability to hide charts and forms based on the user that's viewing the dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-650.avif 650w, https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-650.webp 650w, https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-650.jpeg 650w, https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Dashboard 2.0 Example showing personalised dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/multi-user-dashboard-user2-qTs1QLNtiv-650.jpeg" width="1300" height="752" /></picture></p>
<p>Read more about it here:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-multi-user/">Personalised Multi User Dashboards</a></li>
</ul>
<p>If that wasn't enough, we also have <a href="https://dashboard.flowfuse.com/">rich documentation</a> for Dashboard 2.0 too, detailing all of the available nodes, details on how Dashboard 2.0 is built and how to contribute to the project too if you're that way inclined.</p>
<h2 id="upcoming-webinar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-ga/#upcoming-webinar"></a> Upcoming Webinar</h2>
<p>If you're interested in learning more about Dashboard 2.0 and in particular, personalised multi-user dashboards, we're hosting a webinar on Thursday, 29th February. You can find out more information <a href="https://flowfuse.com/webinars/2024/node-red-dashboard-multi-user/">here</a></p>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/dashboard-2-ga/#follow-our-progress"></a> Follow our Progress</h2>
<p>We aren't stopping here, we'll continnue to push Dashboard 2.0 forward with future development, with a <a href="https://github.com/FlowFuse/node-red-dashboard/issues/12">new UI Gauge</a> next on the list. You can track that progress of that particular issue, and the rest of the work we have lined up on our GitHub Projects:</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/5">Dashboard 1.0 Feature Parity Tracker</a></li>
</ul>
<p>If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/Sentiment Analysis with Node-REDA guide to building a simple sentiment analysis system with Node-RED.2024-01-23T00:00:00ZSumit Shinde<p>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.</p>
<!--more-->
<h2 id="what-exactly-is-sentiment-analysis%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#what-exactly-is-sentiment-analysis%3F"></a> What exactly is sentiment analysis?</h2>
<p>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.</p>
<ul>
<li><strong>Analysing Feedback:</strong> 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.</li>
<li><strong>Campaign Monitoring:</strong> Another use case of sentiment analysis is a measure of influence which is crucial in any marketing campaign.</li>
<li><strong>Brand Monitoring:</strong> 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.</li>
</ul>
<h2 id="building-a-form-in-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#building-a-form-in-dashboard-2.0"></a> Building a Form in Dashboard 2.0</h2>
<p>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.</p>
<ol>
<li>Install Node-RED Dashboard 2.0. Follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">instructions</a> to install.</li>
<li>Drag a ui form widget to the canvas and select the created group.</li>
<li>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.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sentiment-analysis-form-szWddYyGjF-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sentiment-analysis-form-szWddYyGjF-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Taking user input for Sentiment analysis using form" alt=""Taking user input for Sentiment analysis using form"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiment-analysis-form-szWddYyGjF-650.jpeg" width="650" height="424" /></picture></p>
<h2 id="normalizing-the-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#normalizing-the-data"></a> Normalizing the data</h2>
<p>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.</p>
<ol>
<li>Drag a change node to canvas.</li>
<li>Set <code>msg.payload.$FORM_ELEMENT_NAME</code> to <code>msg.payload</code>, replace the <code>$FORM_ELEMENT_NAME</code> with the name of the form element that you have added to the form to obtain user input.</li>
<li>Connect the UI form nodes output to the change node’s input.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sentiment-anlaysis-change-node(1)-sGGRlgRdFg-598.avif 598w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sentiment-anlaysis-change-node(1)-sGGRlgRdFg-598.webp 598w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Normalizing the payload using change node" alt=""Normalizing the payload using change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiment-anlaysis-change-node(1)-sGGRlgRdFg-598.jpeg" width="598" height="390" /></picture></p>
<h2 id="installing-custom-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#installing-custom-node"></a> Installing custom node</h2>
<p>Now it’s time to install a custom node that can perform sentiment analysis for us. In this guide, we will use the <code>node-red-node-sentiment</code> 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.</p>
<ol>
<li>Install the <code>node-red-node-sentiment</code> package by the Node-RED palette manager.</li>
<li>Drag a sentiment node to canvas.</li>
<li>Connect the change nodes output to sentiment node input.</li>
</ol>
<h2 id="calculating-percentage" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#calculating-percentage"></a> Calculating percentage</h2>
<p>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.</p>
<ol>
<li>Drag another change node to canvas.</li>
<li>set <code>msg.payload</code> to <code>((msg.sentiment.score - (-5)) / (5 - (-5))) * 100</code> as a JSONata expression, it will calculate the percentage of the score.</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sentiment-analysis-change-node(2)-fOUBxjaBfl-608.avif 608w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sentiment-analysis-change-node(2)-fOUBxjaBfl-608.webp 608w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Calculating the percentage based on the score using the change node" alt=""Calculating the percentage based on the score using the change node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiment-analysis-change-node(2)-fOUBxjaBfl-608.jpeg" width="608" height="382" /></picture></p>
<h2 id="displaying-result-on-dashboard-2.0" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#displaying-result-on-dashboard-2.0"></a> Displaying result on Dashboard 2.0</h2>
<p>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.</p>
<ol>
<li>Drag a ui template widget to canvas and create another group for it.</li>
<li>Paste the below Vue component snippet into the template widget.</li>
</ol>
<p>We're aware that not everyone coming into Dashboard 2.0 will be familiar with VueJS. We have a more detailed guide <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#building-full-vue-components">here</a>, but we'll also give a quick overview of the component that we'll use to display the result:</p>
<div style="position: relative" id="code-container-144">
<pre class="language-html"><code id="code-144" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-progress-circular</span> <span class="token attr-name">:rotate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>360<span class="token punctuation">"</span></span> <span class="token attr-name">:size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>245<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">:width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>15<span class="token punctuation">"</span></span> <span class="token attr-name">:model-value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg.payload<span class="token punctuation">"</span></span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rgb(0,255,0)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">v-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg.payload <= 33.33<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://i.ibb.co/VHKZ8sn/imgbin-smirk-emoji-face-emoticon-smile-png.png<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sad emoji<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">v-else-if</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg.payload <= 66.66<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://i.ibb.co/nMnybLJ/imgbin-emoji-computer-icons-emoticon-smiley-png.png<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>neutral emoji<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">v-else</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://i.ibb.co/TK12RrH/Smile-Emoji-Face-PNG-Download-Image.png<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>240<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>happy emoji<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-progress-circular</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-144" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ul>
<li>v-progress-circular is a Vuetify component to display a circular progress bar, for a detailed guide refer to our blog on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/">Custom Vuetify components for Dashboard 2.0</a>.</li>
<li><code>rotate</code> is an attribute that lets you specify the rotation angle of the progress bar.</li>
<li><code>size</code> and <code>width</code> allow you to set the size of the circular progress bar, and another <code>width</code> attribute allows you to set the stroke width of the circular progress bar.</li>
<li>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.</li>
</ul>
<p>Your final flow should look like this:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sentiment-anlaysis-flow-VWfC9HkbQu-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sentiment-anlaysis-flow-VWfC9HkbQu-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Node-RED flow to do sentiment analysis" alt=""Node-RED flow to do sentiment analysis"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiment-anlaysis-flow-VWfC9HkbQu-650.jpeg" width="650" height="143" /></picture></p>
<h2 id="deploying-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#deploying-the-flow"></a> Deploying the Flow</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-650.avif 650w, https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-650.webp 650w, https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-650.jpeg 650w, https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Deploying Sentiment analysis Node-RED flow" alt=""Deploying Sentiment analysis Node-RED flow"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiement-analysis-flowfuse-editor-JDMftUY6jN-650.jpeg" width="1300" height="616" /></picture></p>
<p>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 <code>https://<your-instance-name>.flowfuse.cloud/dashboard</code></p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/sentiment-analysis-dashboard-gif-cioCkFeLOX-600.gif 600w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Sentiment analysis on Text using Node-RED Dashboard 2.0" alt=""Sentiment analysis on Text using Node-RED Dashboard 2.0"" loading="lazy" decoding="async" src="https://flowfuse.com/img/sentiment-analysis-dashboard-gif-cioCkFeLOX-600.webp" width="600" height="306" /></picture></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/sentiment-analysis-with-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/Selecting a broker for your Unified NamespaceThe broker is the data backbone for the unified namespace, which one is right for you?2024-01-19T00:00:00ZZJ van de Weg<p>When starting to roll out a new data distribution architecture for the unified namespace (UNS), one of the first questions you'll ask is, "What broker should I select for my UNS? The broker must implement a publish-subscribe (pub-sub) pattern, though that leaves plenty of options.</p>
<!--more-->
<h2 id="technology-selection" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#technology-selection"></a> Technology selection</h2>
<h3 id="two-protocols-frontrunners" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#two-protocols-frontrunners"></a> Two protocols frontrunners</h3>
<p>Currently, there are two protocols that are front runners for becoming the de facto data transfer choice in (industrial) IoT: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">MQTT</a> or <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/">Kafka</a>. They’ve been designed for different use cases and have different properties. At this time, MQTT is more often deployed as a broker in the unified namespace and is generally the best choice when starting to implement a unified namespace, it also features better support from hardware vendors.</p>
<p>First and foremost, MQTT has been designed to enable IoT use cases. The main design objectives were to be lightweight to enable low-bandwidth communication, enable low-power devices, and handle unreliable networks. MQTT enables a large number of data producers and consumers to collaborate.</p>
<p>Kafka is designed as an event streaming platform. Its initial adoption was mostly for data brokers between microservices all part of the same web backend for large sites like LinkedIn. When communicating data between servers or just a few data centers around the world, there’s less of a concern around the reliability of the connection or to enable constrained devices to participate in the shift towards a unified namespace.</p>
<p>Generally, MQTT is more often seen deployed in practice as data distribution architecture.</p>
<h3 id="the-cloud-route" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#the-cloud-route"></a> The Cloud route</h3>
<p>Cloud message queue brokers like AWS Kinesis and GCP Pub/Sub offer a high level of convenience. Scaling the infrastructure for real-time data processing and communication is their concern, the customer is mostly concerned about paying the bill. These brokers are fully managed, meaning they are maintained and updated by the cloud provider, reducing the burden on developers.</p>
<p>However, this convenience comes with the tradeoff of vendor lock-in. When selecting these brokers, the cloud vendor has usually adapted their technology to support many protocols, and these offerings are usually jack-of-all-trades solutions – master of none. It creates a situation where the unified namespace implementation will change in subtle ways to accommodate the vendor instead of the other way around. An organization might become so reliant on a particular vendor's products or services that they are unable to easily switch to another vendor or protocol that serves their business objectives better. The cost of changing is exacerbated by having to train personnel on new and open-source protocols.</p>
<p>In addition to vendor lock-in, cloud message queue brokers also introduce reliance on the network to the cloud providers. Network reliability for (industrial) IoT is a major concern due to the physically distributed nature, adding external dependencies creates more variability.</p>
<h3 id="exotic-options" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#exotic-options"></a> Exotic options</h3>
<p>RabbitMQ is a widely used open-source message broker that’s mostly used as an event message bus for web applications. It can also function as a hub in a unified namespace. The broker primarily supports the <a href="https://flowfuse.com/node-red/protocol/amqp/">AMQP</a> (Advanced Message Queuing Protocol), considered the industry standard for high-performance messaging systems. It also supports STOMP (Streaming Text Oriented Messaging Protocol) and MQTT (MQ Telemetry Transport), catering to various messaging needs.</p>
<p>NATS, short for Network Agnostic Messaging System, is another open-source message broker that is designed for simplicity and reliability. NATS implements its own protocols, making it harder to be interoperable with hardware and software previously purchased. NATS has requirements on message structure too, which creates another barrier to adoption for IoT use cases.</p>
<h2 id="how-node-red-helps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#how-node-red-helps"></a> How Node-RED Helps</h2>
<p>Node-RED provides a powerful and flexible way to integrate with various brokers, supporting protocols such as <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/06/how-to-use-mqtt-in-node-red/">MQTT</a>, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/using-kafka-with-node-red/">Kafka</a>, and <a href="https://flowfuse.com/node-red/protocol/amqp/">AMQP</a>. It allows you to build and manage workflows that interact with your chosen broker, seamlessly connecting different data sources and systems.</p>
<p>However, using Node-RED alone in production environments requires additional considerations, such as server deployment, instance management, security implementation, and scalability. This is where FlowFuse enhances Node-RED's capabilities by adding production-ready features. FlowFuse simplifies managing and deploying Node-RED applications, providing essential functionalities like scalability, robust security, and efficient collaboration tools.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and experience how FlowFuse can streamline your Node-RED deployments and management.</strong></p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/unified-namespace-what-broker/#conclusion"></a> Conclusion</h2>
<p>An MQTT broker is currently recommended as a broker solution for your unified namespace. There are many different implementations of the protocol available. At FlowFuse, we’re using <a href="https://mosquitto.org/">Mosquitto</a>, due to its efficiency on resources and flexible authentication layer. Further, our customers are reporting to be happy with <a href="https://www.emqx.io/">EMqX</a>, which is written in Erlang – itself a messaging-oriented programming language – and has been put through its paces in practice. If you’re dipping your toes into the unified namespace, either of those or another MQTT broker is currently recommended.</p>
<p>Note that it’s recommended to allow yourself flexibility in the broker, and treat it as a message-passing system, and your organization will be able to easily swap it out later if any other broker is a better fit later on.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/FlowFuse 2.0 ReleaseElevating Node-RED Device Management to new heights2024-01-18T00:00:00Z<p>Following the release of FlowFuse 1.0 end of 2022, we're excited to release FlowFuse 2.0, marking a significant step in managing Node-RED remote instances, which we call Devices. FlowFuse already was the best place to operate Node-RED at scale in the cloud or on-premise, now it's able to manage Node-RED where ever it's run.</p>
<!--more-->
<p>Many organizations position Node-RED instances on remote servers like edge or industrial devices. This way they can meet network requirement, interact with analog protocols, and overcome other infrastructure requirements. Management of remote instances is crucial for the overall success of closing the gap between IT and OT. A key enhancement was the introduction of Device Groups (from version 1.15) and the new feature to assign target snapshots. This allows for direct and streamlined management of Node-RED Device fleets, setting the stage for future advancements in device management capabilities.</p>
<p>For our FlowFuse users, this means it is no longer necessary or recommended to assign devices to an instance. Node-RED devices can be managed independently, and snapshots can be assigned via DevOps pipelines.</p>
<h2 id="enterprise-readiness" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#enterprise-readiness"></a> Enterprise-Readiness</h2>
<p>FlowFuse is committed to augmenting the enterprise-readiness of Node-RED with introductions like <a href="https://flowfuse.com/docs/admin/sso/">Single Sign-On (SSO)</a>, <a href="https://flowfuse.com/docs/user/user-settings/#two-factor-authentication">Multi-Factor Authentication (MFA)</a>, and <a href="https://flowfuse.com/docs/user/high-availability/">High Availability</a> since version 1.0. Furthermore, we recently achieved <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/">SOC2 Type 1 compliance</a>. With these advancements, Node-RED, in combination with FlowFuse, is genuinely ready for enterprise and production use.</p>
<h2 id="enhanced-integration-capabilities" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#enhanced-integration-capabilities"></a> Enhanced Integration Capabilities</h2>
<p>The Node-RED Flow Library has always been a cornerstone, offering over 4800 connectors (nodes) for various OT and IT protocols. Thanks to the community and the Node-RED library. Building on this foundation, FlowFuse introduced "Certified Nodes" and "Blueprints". These <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/">Blueprints</a> are designed to provide an easier start with Node-RED, showcasing its full potential, while Certified Nodes ensure the security of the nodes used. Learn more about our new Certified Nodes <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/">here</a>.</p>
<h2 id="developer-velocity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#developer-velocity"></a> Developer Velocity</h2>
<p>Node-RED team development is made possible with FlowFuse. Different development team members are able to share and collaborate on the same Node-RED instance. This makes for much easier collaboration between Node-RED developers. We've worked hard on maturing our <a href="https://flowfuse.com/docs/user/snapshots/">snapshot capabilties</a> and introduced <a href="https://flowfuse.com/docs/user/devops-pipelines/">DevOps Pipelines</a> that can be set up to stage Node-RED instances that have different development stages, e.g. test, development and production.</p>
<h2 id="looking-ahead" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#looking-ahead"></a> Looking Ahead</h2>
<p>At FlowFuse, our mission is to empower bottom-up innovation and enable organizations to transform their workflows into business-critical applications with unprecedented efficiency. As we move forward, we are excited to invite our users to actively engage with our future developments. Our <a href="https://flowfuse.com/changelog/">Roadmap</a> lays out the advancements we're targeting, offering a glimpse into the features and enhancements that are on the horizon. We also encourage our users to stay informed and involved by checking out our latest updates in our detailed <a href="https://flowfuse.com/changelog/">changelog</a>. Your insights and feedback are crucial to us; they fuel our commitment to continuous improvement and innovation. We warmly invite you to <a href="https://flowfuse.com/contact-us/">share your thoughts and suggestions</a>, as your input is a vital part of our journey in shaping the next steps for FlowFuse.</p>
<h2 id="how-to-get-started" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#how-to-get-started"></a> How to get started</h2>
<p>You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running version 2.0.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<p>The version 2.0 release of the FlowFuse Helm Chart includes a breaking change for deployments making use of the <code>forge.localPostgresql</code> setting when upgrading. This is where the helm chart installs a dedicated PostgreSQL database instance.
With version 2.0 we have updated the version of the Bitnami PostgreSQL Helm sub-chart we bundle and the upgrade process will require some manual intervention to ensure things work correctly. A fresh install should not require any extra steps.</p>
<p>The steps are documented on the <a href="https://flowfuse.com/docs/install/kubernetes/#upgrade">Upgrade instructions</a> page, please read them carefully before upgrading</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/flowfuse-release-2-0/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/Capture Data from edge devices with Node-REDFlowFuse allows you to run Node-RED anywhere to capture all the data2024-01-17T00:00:00ZZJ van de Weg<p>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.</p>
<!--more-->
<p>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.</p>
<h2 id="installing-the-flowfuse-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/#installing-the-flowfuse-agent"></a> Installing the FlowFuse agent</h2>
<p>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.</p>
<p>The device agent can run anywhere you can run a Docker container or Node.JS runtime (version 16.0+) can be installed.</p>
<h3 id="registering-a-device-on-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/#registering-a-device-on-flowfuse"></a> Registering a device on FlowFuse</h3>
<p>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 <code>device.yml</code> 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowfuse-agent-setup-cwqx7_w6a8-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowfuse-agent-setup-cwqx7_w6a8-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Setting up a FlowFuse agent" alt="Setting up a FlowFuse agent" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowfuse-agent-setup-cwqx7_w6a8-650.jpeg" width="650" height="505" /></picture></p>
<p>FlowFuse will prompt you to add a name (required), and a type (not required). When you’ve clicked <code>Add</code> you’ll get a new dialog to download the required file.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/device-yml-flowfuse-tJYk8TEO24-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/device-yml-flowfuse-tJYk8TEO24-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The contents of a device.yml file" alt="Configuration file for the FlowFuse agent" loading="lazy" decoding="async" src="https://flowfuse.com/img/device-yml-flowfuse-tJYk8TEO24-650.jpeg" width="650" height="457" /></picture></p>
<h3 id="install-the-flowfuse-agent-through-docker" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/#install-the-flowfuse-agent-through-docker"></a> Install the FlowFuse agent through Docker</h3>
<p>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 <code>/opt/flowfuse/device.yml</code>. Start the agent by running:</p>
<pre><code>docker run --mount type=bind,src=/path/to/device.yml,target=/opt/flowfuse-device/device.yml -p 1880:1880 flowfuse/device-agent:latest
</code></pre>
<p>Note that for production cases, ensure the container is restarted on reboot. Docker can do this for you, <a href="https://docs.docker.com/config/containers/start-containers-automatically/">please follow their guide</a>.</p>
<h3 id="install-the-flowfuse-agent-with-npm" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/#install-the-flowfuse-agent-with-npm"></a> Install the FlowFuse agent with npm</h3>
<p>To install the agent through NPM, you’ll need a Node.JS version of 18.0 or later. Open a command prompt and run: <code>npm install -g @flowfuse/device-agent</code>.</p>
<p>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.</p>
<p>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 <code>device.yml</code> file that’s needed now. On Linux or Mac, move the file to <code>/opt/flowfuse-device/device.yml</code>, and for Windows-based systems, move the file to <code>c:\opt\flowfuse-device\device.yml</code>.</p>
<p>Afterward, start the agent with: <code>flowfuse-device-agent</code>.</p>
<p>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.</p>
<h3 id="programming-flows-for-the-edge" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/capture-data-edge-with-node-red-flowfuse/#programming-flows-for-the-edge"></a> Programming flows for the edge</h3>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/FlowFuse is now SOC 2 Type 1 CompliantFlowFuse's Path to SOC 2 Type 1 Compliance - A Testament to Our Commitment to Securing Customer and User Data.2024-01-15T00:00:00Z<p>FlowFuse achieved SOC 2 type 1 compliance! SOC 2, governed by the American Institute of Certified Public Accountants (AICPA), is a crucial framework for organizations handling customer data.
An independent audit assessed that FlowFuse's controls are effectively designed and operationally applied. Achieving SOC 2 Type 1 compliance validates our practises as an business and provides our customers assurances we apply the highest standards to ensure their data is protected.</p>
<!--more-->
<h2 id="improving-our-security-posture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/#improving-our-security-posture"></a> Improving Our Security Posture</h2>
<p>At FlowFuse, we understand that professionalizing Node-RED deployments for our clients means adhering to the highest standards, including SOC 2 requirements. This commitment is at the core of our security philosophy. In a world rife with cybersecurity threats and data breaches, taking information security seriously isn't just an option—it's a critical necessity. Our SOC 2 audit was far more than just a procedural step. It represented a comprehensive, independent third-party validation of our robust controls and processes. We believe in transparency and accountability, which is why we document our policies in our open handbook, inviting scrutiny from vendors and reinforcing trust with our customers. Providing this level of independent audit not only serves our customers better and more efficiently but also offers FlowFuse valuable insights into enhancing our security measures and identifying any gaps in our policies. This proactive approach ensures we continue keeping your data safe and secure at all times.
As we continue to grow and evolve, ensuring the security of our systems and data becomes ever more critical. The next step on FlowFuse's journey to provide independant proof we're on the right track: We're currently in the observation phase of the SOC2 type 2.</p>
<p>SOC 2 Type 1 assesses the design of an organization's security controls at a specific point in time, while SOC 2 Type 2 evaluates the effectiveness of those controls over a period of time, typically three to twelve months.</p>
<h2 id="flowfuse's-journey-to-soc-2-compliance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/#flowfuse's-journey-to-soc-2-compliance"></a> FlowFuse's Journey to SOC 2 Compliance</h2>
<h3 id="compliance-partners" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/#compliance-partners"></a> Compliance Partners</h3>
<p>The independent audit was performed by Advantage Partners. Their expertise played a large role in our successful attainment of this certification. Before the audit was performed the company went through an extensive process to uncover what policies were missing, required updating, or were already in place. Further, lots of tribal knowledge has been written down and is now enforced by internal policies. For example</p>
<ol>
<li><a href="https://flowfuse.com/handbook/company/security/data-management/#data-management-policy">Data Management Policy</a></li>
<li><a href="https://flowfuse.com/handbook/company/security/access-control/#access-control-policy">Access Control Policy</a></li>
<li><a href="https://flowfuse.com/handbook/company/security/incident-response/#incident-response-plan">Incident Response Policy</a></li>
<li><a href="https://flowfuse.com/handbook/company/security/human-resources/#human-resources-security-policy">Human Resources Security Policy</a></li>
</ol>
<p>It's been a team effort from engineering to updated HR polices!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/Send a File to Node-REDA guide to sending a CSV file to Node-RED and start interacting with it.2024-01-05T00:00:00Z<p>Have you ever needed to send a CSV file to your Node-RED instance? This file can go on to populate a shift schedule, product specifications, or some other configuration file that is used. In this guide, we provide a couple of options to upload the data to your Node-RED for further processing and to organize the data to be sent on or used.</p>
<!--more-->
<h2 id="why-would-you-need-to-send-a-file-to-node-red%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#why-would-you-need-to-send-a-file-to-node-red%3F"></a> Why would you need to send a file to Node-RED?</h2>
<p>Often times it is necessary to update lookup tables in a SQL database, but you don't necessarily want to give access to everyone to edit the database, nor do you want to have to do it all yourself. This can often be seen when new products are introduced into a manufacturing facility. It may not be often, but enough that it warrants its own application. This process will guide you in a way that will enable your teammates to upload the files to the system themselves.</p>
<p>Furthermore, on the management layer of most companies, Excel and Google Sheets are the go-to tools to perform data collection tasks. Getting management involved in processes might require you to build an import feature for them. Asking your manager to "Save as" CSV is much easier than teaching them SQL!</p>
<h2 id="2-ways-to-send-a-file-to-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#2-ways-to-send-a-file-to-node-red"></a> 2 Ways to send a file to Node-RED</h2>
<p>There are many approaches that can be taken when solving this. We are going to go over 2 here.</p>
<ol>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#simple-python-script">Simple Python Script</a> - Simple script that will be shared below. It is a simple Python application that allows the user to send a file with a simple command, but this might require a little more technical skills that the end user may not feel comfortable with.</li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#stand-alone-web-application">Stand Alone Web Application</a> - A web-based application that allows the user to upload files to a browser with a selectable endpoint.</li>
</ol>
<h3 id="simple-python-script" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#simple-python-script"></a> Simple Python Script</h3>
<p>This simple Python script sends a file to a Node-RED flow. The flow that will work with this script can be seen <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#node-red-ingress">here</a>.</p>
<p>The script requires <strong>requests</strong> and <strong>Python 3.x</strong>.</p>
<p>Install requests:</p>
<div style="position: relative" id="code-container-43">
<pre class="language-bash"><code id="code-43" class="language-bash">pip <span class="token function">install</span> requests</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-43" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Create a file called run.py and paste the contents into the file.</p>
<div style="position: relative" id="code-container-47">
<pre class="language-python"><code id="code-47" class="language-python"><span class="token keyword">import</span> requests<br /><br /><span class="token keyword">def</span> <span class="token function">send_file</span><span class="token punctuation">(</span>nodered_url<span class="token punctuation">,</span> file_path<span class="token punctuation">)</span><span class="token punctuation">:</span><br /> <span class="token comment"># Open the file in binary mode</span><br /> <span class="token keyword">with</span> <span class="token builtin">open</span><span class="token punctuation">(</span>file_path<span class="token punctuation">,</span> <span class="token string">'rb'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token builtin">file</span><span class="token punctuation">:</span><br /> files <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token string">'file'</span><span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token builtin">file</span><span class="token punctuation">.</span>name<span class="token punctuation">,</span> <span class="token builtin">file</span><span class="token punctuation">,</span> <span class="token string">'multipart/form-data'</span><span class="token punctuation">)</span><span class="token punctuation">}</span><br /> response <span class="token operator">=</span> requests<span class="token punctuation">.</span>post<span class="token punctuation">(</span>nodered_url<span class="token punctuation">,</span> files<span class="token operator">=</span>files<span class="token punctuation">)</span><br /><br /> <span class="token keyword">return</span> response<br /><br /><span class="token comment"># Update the ip address and port of your Node-RED instance</span><br />nodered_url <span class="token operator">=</span> <span class="token string">'http://localhost:1880/fileupload'</span><br /><br /><span class="token comment"># Update the location of your file</span><br />file_path <span class="token operator">=</span> <span class="token string">'C:/Users/myUser/Downloads/shiftSchedule.csv'</span><br /><br />response <span class="token operator">=</span> send_file<span class="token punctuation">(</span>nodered_url<span class="token punctuation">,</span> file_path<span class="token punctuation">)</span><br /><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Response Status Code: </span><span class="token interpolation"><span class="token punctuation">{</span>response<span class="token punctuation">.</span>status_code<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><br /><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Response Body: </span><span class="token interpolation"><span class="token punctuation">{</span>response<span class="token punctuation">.</span>text<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-47" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Update the <strong>nodered_url</strong> to the location of the Node-RED instance. Be sure to adjust the port if the default port of 1880 isn't being used.</p>
<p>Update the <strong>file_path</strong> with the path to where the file to be uploaded will be located.</p>
<p><strong>Save</strong></p>
<p>To run:</p>
<div style="position: relative" id="code-container-60">
<pre class="language-python"><code id="code-60" class="language-python">python run<span class="token punctuation">.</span>py</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-60" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="stand-alone-web-application" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#stand-alone-web-application"></a> Stand Alone Web Application</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/csv_upload_app-WEYnBQL1ty-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/csv_upload_app-WEYnBQL1ty-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="csv upload application" loading="lazy" decoding="async" src="https://flowfuse.com/img/csv_upload_app-WEYnBQL1ty-650.jpeg" width="650" height="319" /></picture></p>
<p>This stand-alone web application can be run on either Windows or Linux, .bat for Windows, and .sh for Linux.</p>
<h4 id="installation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#installation"></a> Installation</h4>
<p>Clone the repository and navigate to the directory:</p>
<div style="position: relative" id="code-container-76">
<pre class="language-bash"><code id="code-76" class="language-bash"><span class="token function">git</span> clone https://github.com/gdziuba/FF_Send-File-to-NR.git <span class="token operator">&&</span> <span class="token builtin class-name">cd</span> FF_Send-File-to-NR</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-76" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="configuration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#configuration"></a> Configuration</h4>
<p>Edit the lines in the body of <a href="https://github.com/gdziuba/FF_Send-File-to-NR/blob/21214f88c6c4536f49efb88cf5f84bf52071a88b/templates/index.html#L69">index.html</a> to include the endpoints to which you would like to send the files.</p>
<pre><code><option value="http://localhost:1880/fileupload">CSV File Upload</option>
</code></pre>
<h3 id="operating-systems" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#operating-systems"></a> Operating Systems</h3>
<h4 id="windows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#windows"></a> Windows</h4>
<p>Run the script:</p>
<div style="position: relative" id="code-container-93">
<pre class="language-bash"><code id="code-93" class="language-bash">.<span class="token punctuation">\</span>start_app.bat</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-93" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will install if necessary, start the Flask Application, and take you to localhost:5000 on the browser.</p>
<h4 id="linux" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#linux"></a> Linux</h4>
<p>Make the script executable by running running:</p>
<div style="position: relative" id="code-container-103">
<pre class="language-bash"><code id="code-103" class="language-bash"><span class="token function">chmod</span> +x setup_and_run.sh</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-103" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Then run the application with:</p>
<div style="position: relative" id="code-container-107">
<pre class="language-bash"><code id="code-107" class="language-bash">./setup_and_run.sh</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-107" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>To access the application, open a browser to the <strong><node-red-host-ip>:5000</strong> of the running application.</p>
<h4 id="node-red-ingress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#node-red-ingress"></a> Node-RED Ingress</h4>
<iframe width="100%" height="225px" src="https://flows.nodered.org/flow/effb53752e5d6f767b3c7e5d41a4a6e8/share?height=100" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<p>Once we have a file ready to be sent, we now need to configure the receiving side in Node-RED. In this example, we are leveraging a CSV formatted file and then converting it to be used at a later time.</p>
<p>A link to the flow can be found <a href="https://flows.nodered.org/flow/effb53752e5d6f767b3c7e5d41a4a6e8">here</a>.</p>
<p>To import the flow, follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#1.-copy-and-share-your-flows-using-export-and-import">instructions</a>.</p>
<p>A Simple HTTP In node can be used in the form of a Post, ensuring the configuration allows for a file.</p>
<h2 id="wanna-import-it-directly-into-your-node-red-instance-via-a-dashboard%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/#wanna-import-it-directly-into-your-node-red-instance-via-a-dashboard%3F"></a> Wanna import it directly into your Node-RED instance via a Dashboard?</h2>
<p>Check out this <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/">blog</a> on how to directly import a file into a Node-RED instance via Dashboard 2.0.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/Import a File into Node-RED with Dashboard 2.0Use Dashboard 2.0 to import a CSV file into Node-RED.2024-01-05T00:00:00Z<p>Need to get a file into Node-RED, but don't want to over complicate things. This article outlines how you can leverage Dashboard 2.0 to import a file directly into Node-RED via a Dashboard.</p>
<!--more-->
<h2 id="why-would-you-need-to-import-a-file-to-node-red%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/#why-would-you-need-to-import-a-file-to-node-red%3F"></a> Why would you need to import a file to Node-RED?</h2>
<p>Often times it is necessary to update lookup tables in a SQL database, but you don't necessarily want to give access to everyone to edit the database, nor do you want to have to do it all yourself. This can often be seen when new products are introduced into a manufacturing facility. It may not be often, but enough that it warrants its own application. This process will guide you in a way that will enable your teammates to upload the files to the system themselves.</p>
<p>Furthermore, on the management layer of most companies, Excel and Google Sheets are the go-to tools to perform data collection tasks. Getting management involved in processes might require you to build an import feature for them. Asking your manager to "Save as" CSV is much easier than teaching them SQL!</p>
<h3 id="node-red-dashboard-(flowfuse)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/#node-red-dashboard-(flowfuse)"></a> Node-RED Dashboard (FlowFuse)</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/csv-dashboard-2SGfaHECgf-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/csv-dashboard-2SGfaHECgf-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="csv dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/csv-dashboard-2SGfaHECgf-650.jpeg" width="650" height="421" /></picture></p>
<p>This simple flow allows the user to visualize data from a CSV in the Node-RED Dashboard. The button then allows the user to initiate a request to send the data to the next step. This next step could be anything from loading into a SQL database to saving it.</p>
<h3 id="instructions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/#instructions"></a> Instructions</h3>
<ol>
<li>Install Node-RED Dashboard 2.0. Follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">instructions</a> to install.</li>
<li>Import Flow - to import the flow into your Node-RED instance follow these <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#1.-copy-and-share-your-flows-using-export-and-import">instructions</a>.</li>
<li>Access Dashboard - To access the dashboard, navigate to the <code>https://<flowfuse-instance-name>.flowfuse.cloud/dashboard</code>.</li>
</ol>
<p>This dashboard is currently configured to take in CSV files and transform them into a single message that is sent to the table for visualization. Simultaneously the data from the import is stored locally in the flow context. From there, the button can be used to trigger the sending of the data from the flow context to the next destination. In this case, it is a simple debug node.</p>
<iframe width="100%" height="225px" src="https://flows.nodered.org/flow/8c505039ac1b8dbed2bee1e22ee2975a/share?height=100" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<h2 id="need-to-send-a-file-to-node-red-from-another-application-or-source%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/import-a-file/#need-to-send-a-file-to-node-red-from-another-application-or-source%3F"></a> Need to Send a File to Node-RED from another application or source?</h2>
<p>Check out this <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/send-a-file/">blog</a> on how to send a file from either a stand alone web application or use the sample python script to imbed it into your current application.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/Data Modeling for your Unified NamespaceHow to use FlowFuse as your Schema Registry?2023-12-27T00:00:00Z<p>In the realm of industrial manufacturing, the concept of a Unified Namespace (UNS) emerges as a pivotal instrument for enhanced communication within a manufacturing network framework. Predicated on an event-driven architectural model, this approach advocates for the universal accessibility of data, irrespective of the immediate presence of a data consumer.</p>
<!--more-->
<p>This paradigm allows for a flexible role allocation within the network, where nodes can dynamically switch between being data producers and consumers, contingent upon the fluctuating requirements of the system at any specific juncture. For those unfamiliar with UNS, I recommend revisiting my <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/">previous article</a> on the subject.</p>
<p>This article aims to explain the process of data modeling for your UNS, highlighting the role of tools like the FlowFuse Team Library in schema management.</p>
<p><strong>Overview of Steps:</strong></p>
<ol>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-1---connection-to-your-operational-technology-(ot)-equipment">Connection to your Operational Technology (OT) equipment</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-2---structuring-your-payload">Structuring your payload</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-3---building-your-topic-hierarchy">Building your Topic Hierarchy</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-4---connection-to-your-unified-namespace">Connection to your Unified Namespace</a></li>
</ol>
<h2 id="step-1---connection-to-your-operational-technology-(ot)-equipment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-1---connection-to-your-operational-technology-(ot)-equipment"></a> Step 1 - Connection to your Operational Technology (OT) equipment</h2>
<p>The journey begins with establishing connections to OT equipment, which may include Programmable Logic Controllers (PLCs), Historian databases, and sensors. It is essential to facilitate compatibility with a diverse array of protocols. In this context, Node-RED emerges as a pivotal tool, bolstered by its expansive community-generated catalog featuring over 4500 nodes.</p>
<p>In my example, the focus is on integration with a RevolutionPi. To achieve this, the FlowFuse Device Agent was deployed on a RevolutionPi (see our <a href="https://flowfuse.com/docs/hardware/raspbian/">documentation</a>), and specific RevolutionPi nodes were installed. These nodes enable direct interaction with all interfaces of the PLC and are available through the <a href="https://flows.nodered.org/node/node-red-contrib-revpi-nodes">Node-RED library</a>. Subsequent steps involved acquiring temperature data directly from the PLC.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/revpi_nodes--rhb7iF9dw-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/revpi_nodes--rhb7iF9dw-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/revpi_nodes--rhb7iF9dw-650.jpeg" width="650" height="277" /></picture></p>
<p>For optimal data accuracy and integrity, it is recommended to timestamp data at the point of origin. In our scenario, the PLC outputs lack inherent timestamping. Consequently, I integrated a timestamp at the data acquisition stage within Node-RED, which runs on the same hardware.</p>
<p>A general recommendation is the imperative of maintaining data integrity during transmission from OT systems to the message broker. This is particularly salient in regulated sectors such as pharmaceuticals, where standards like GxP mandate the preservation of unaltered data during transfer to the UNS.</p>
<h2 id="step-2---structuring-your-payload" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-2---structuring-your-payload"></a> Step 2 - Structuring your payload</h2>
<p>The payload is the core of transmitted data. Transforming the payload for mutual intelligibility between sender and receiver, even within the same protocol, is sometimes necessary. Standardizing payload formats ensures consistent data storage and transmission. I recommend including schema type information with the data to cater to diverse use cases.</p>
<p>Utilizing FlowFuse and Node-RED can enforce schema consistency. Node-RED's template node lets you define JSON schemas for your flows, while the FlowFuse Team Library facilitates schema sharing and consistency across your organization.</p>
<p>In my example, I use a very simple JSON schema as a structure for measurements for <code>StationA</code>:</p>
<div style="position: relative" id="code-container-65">
<pre class="language-json"><code id="code-65" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"$schema"</span><span class="token operator">:</span> <span class="token string">"Enterprise/Site/Line1/StationA/measurements/_schema"</span><span class="token punctuation">,</span><br /> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token string">"Measurement Schema for StationA"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span><br /> <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The actual value being measured"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"number"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The unit of the measurement value"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"The timestamp of the measurement in ISO 8601 format"</span><span class="token punctuation">,</span><br /> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token property">"required"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token string">"value"</span><span class="token punctuation">,</span><br /> <span class="token string">"unit"</span><span class="token punctuation">,</span><br /> <span class="token string">"timestamp"</span><br /> <span class="token punctuation">]</span><br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-65" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Example Data:</p>
<div style="position: relative" id="code-container-69">
<pre class="language-json"><code id="code-69" class="language-json"><span class="token punctuation">{</span><br /> <span class="token property">"value"</span><span class="token operator">:</span> msg.payload<span class="token punctuation">,</span><br /> <span class="token property">"unit"</span><span class="token operator">:</span> <span class="token string">"Celsius"</span><span class="token punctuation">,</span><br /> <span class="token property">"timestamp"</span><span class="token operator">:</span> msg.timestamp<br /><span class="token punctuation">}</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-69" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/template_node-LJCACM0E8d-377.avif 377w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/template_node-LJCACM0E8d-377.webp 377w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Node-RED template node" loading="lazy" decoding="async" src="https://flowfuse.com/img/template_node-LJCACM0E8d-377.jpeg" width="377" height="188" /></picture></p>
<p>The FlowFuse Team Library acts as my schema registry within my organization, allowing me to reuse my schemas and ensure consistency.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/team_library-HAn0-JG1C7-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/team_library-HAn0-JG1C7-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="FlowFuse Team Library" loading="lazy" decoding="async" src="https://flowfuse.com/img/team_library-HAn0-JG1C7-650.jpeg" width="650" height="294" /></picture></p>
<h2 id="step-3---building-your-topic-hierarchy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-3---building-your-topic-hierarchy"></a> Step 3 - Building your Topic Hierarchy</h2>
<p>Your topic hierarchy should reflect your physical plant structure or align with existing asset naming systems. This approach improves data visibility and eases navigation for OT engineers. Many enterprises opt for the <a href="https://www.isa.org/products/ansi-isa-95-00-02-2018-enterprise-control-system-i">ISA-95 part 2</a> model to structure their topics.</p>
<p>In our example, we follow the structure of: Enterprise/Site/Line1/StationA</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-650.avif 650w, https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-650.webp 650w, https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-650.jpeg 650w, https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="MQTT Topic Tree" loading="lazy" decoding="async" src="https://flowfuse.com/img/mqtt_topic_tree-PvfzIAhzzE-650.jpeg" width="1300" height="213" /></picture></p>
<h2 id="step-4---connection-to-your-unified-namespace" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#step-4---connection-to-your-unified-namespace"></a> Step 4 - Connection to your Unified Namespace</h2>
<p>Finally, transfer your data to the UNS, using protocols like MQTT or Kafka, depending on your UNS setup. While MQTT can handle up to 256 MB per payload, Kafka's default is 1MB, expandable to 10MB. These capacities suffice for most data types. In our example, we'll employ MQTT.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#conclusion"></a> Conclusion</h2>
<p>In conclusion, implementing a Unified Namespace (UNS) with efficient data modeling is a transformative step for any industrial manufacturing setup. By leveraging tools like FlowFuse Team Library, Node-RED, and protocols such as MQTT or Kafka, organizations can achieve a harmonious data ecosystem where information flows seamlessly across various nodes. As illustrated through practical examples, including the integration with a RevolutionPi, the importance of standardizing data schemas, maintaining data integrity, and structuring topic hierarchies cannot be overstated. Embracing these practices not only enhances operational efficiency but also paves the way for more advanced analytics and machine learning applications.</p>
<h3 id="the-complete-node-red-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/unified-namespace-data-modelling/#the-complete-node-red-flow"></a> The complete Node-RED flow</h3>
<iframe width="100%" height="225px" src="https://flows.nodered.org/flow/f6c783c6e9c1863145e0c63418eb5fe5/share?height=100" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/Thank you for an incredible 2023!Reviewing an amazing 2023 for FlowFuse2023-12-22T00:00:00ZZJ van de Weg<p>At the end of the year there’s always an opportunity to review how the year went, and I'm gonna take this opportunity to share the review of 2023 for FlowFuse. It's been an incredible year for FlowFuse and we've achieved a lot with our team.</p>
<!--more-->
<h3 id="flowfuse-branding" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/#flowfuse-branding"></a> FlowFuse branding</h3>
<p>First off; in 2023 we were known as FlowForge. But, due to some trademark challenges, we went through a <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/">rebranding phase</a> over the summer. It's been a bit of an adjustment, and you might still catch us – and even some of our customers – occasionally slipping up with the old name. But overall, we're really pleased with how smoothly everything's transitioned.</p>
<h3 id="product-adoption" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/#product-adoption"></a> Product adoption</h3>
<p>On the product side: Growth in adoption of the FlowFuse platform has been tremendous, in many dimensions; revenue generated, customers onboarded, and how many users are now professionalizing their usage of Node-RED. Our development platform has been used by thousands of developers to acquire data from various sources and visualize it to build rich applications for their use cases. And at every step of the way we’ve been able to improve their experience.</p>
<p>Data acquisition was always possible without FlowFuse, however we’ve improved on the status quo through the <a href="https://flowfuse.com/certified-nodes/">Certified Nodes</a> program. A nascent program that vets often used custom nodes from the community to ensure business readiness and validate nodes to ensure there’s no malicious code installed.</p>
<p>Further, this year we’ve started <a href="https://dashboard.flowfuse.com/">Dashboard 2.0</a>. The development of the successor of <a href="https://flows.nodered.org/node/node-red-dashboard">node-red-dashboard</a>, which is built on deprecated technology and effectively on life-support. The development of the new Dashboard technology has taken massive steps and it’s very stable. Feature parity is not yet achieved, though we’re happy with the adoption of Dashboard 2.0 and the community <a href="https://github.com/FlowFuse/node-red-dashboard/issues">reporting issues and improvements</a>.</p>
<p>With the product improvements to FlowFuse, we’ve empowered large audiences to try and adopt the product. We’ve seen adoption in many areas:</p>
<ol>
<li>Process manufacturing – Mining, oil & gas, beverages.</li>
<li>Discrete manufacturing – Ranging from automotive to logistics use-cases.</li>
<li>Digital transformation – Digital only integration, from website back-ends to workflow engines for up-skilled employees.</li>
<li>Agriculture – From environment monitoring to controlling sprinklers, water-pumps, and more.</li>
<li>Education & Research – Hundreds of students have registered for FlowFuse to learn how IIoT works, start building solutions, and prepare themselves for employing these skills in their first jobs.</li>
</ol>
<p>We’re always happy to <a href="https://flowfuse.com/contact-us/">support you</a> in any of these or other industries where you may find value in how we streamline operations, manage your data acquisition logic and roll out, and remain compliant during your journey.</p>
<h3 id="pricing-changes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/#pricing-changes"></a> Pricing changes</h3>
<p>A driving force behind the horizontal nature of adoption of the FlowFuse development platform has been the introduction of the 3 product tiers earlier this year. We’ve introduced a Starter package, Team and Enterprise tiers.</p>
<p>The <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/">starter package</a> allows everyone to adopt the core features of FlowFuse and Node-RED. It provides hosting of 2 Node-RED instances in the Cloud and management of 2 Node-REDs on the edge.</p>
<p>A user can be promoted to the <a href="https://flowfuse.com/changelog/2023/09/introduction-enterprise-tier/">Team or Enterprise tier</a> when they’re ready to further professionalize their adoption and have access to enhanced compliance, faster time to value for their developers, among other features.</p>
<h3 id="what-2024-will-bring" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/#what-2024-will-bring"></a> What 2024 will bring</h3>
<p>FlowFuse will continue to support the Node-RED community at large. In 2024 we’re looking to further grow into the main development platform for low-code developers. While our main focus will remain around Node-RED, there’s more to be done to become the defacto low-code development platform. Core of which are a few pillars:</p>
<ol>
<li>Time to value for developers</li>
<li>Empowering more employees to automate the software layer of the solution</li>
<li>Enhanced compliance and enforcement</li>
</ol>
<p>We’re hoping to continue to serve our customers and grow the customer base in 2024!</p>
<h3 id="holiday-season-support" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/flowfuse-year-review-2023/#holiday-season-support"></a> Holiday Season Support</h3>
<p>Over the next couple of weeks (22nd December - 2nd January), most of our team will be taking some well deserved time off. Don't worry, we will still be available if you need emergency support. The best way to contact us is via our website's <a href="https://flowfuse.com/support/">support page</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/Introduction to the Unified NamespaceMaking data available for Industry 4.0 use-cases2023-12-20T00:00:00ZZJ van de Weg<p>As your organization is generating more data there’s key architectural
decisions to be made to ensure the full value can be unlocked and you’re
leveraging not just the tip of the iceberg. The Unified Namespace (UNS)
provides a blueprint to allow data to be consumed by many data-consumers.
FlowFuse helps you manage this migration and the operationalization of your
data.</p>
<!--more-->
<p>To facilitate a many to many connection between data producers and data consumers, there are two changes to be made to your architecture:</p>
<ol>
<li>Data transport through a hub-and-spokes model</li>
<li>Set structure of the Data</li>
</ol>
<h2 id="hub-and-spokes-model-replaces-point-to-point" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/#hub-and-spokes-model-replaces-point-to-point"></a> Hub and spokes model replaces Point to Point</h2>
<p>Traditionally, for example web servers serving web pages, the client requests a
page from a server. This is a point to point connection between those two parties.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/uns-point-to-point-teL84klihq-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/uns-point-to-point-teL84klihq-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Point to Point connection" alt="Point to point graphic" loading="lazy" decoding="async" src="https://flowfuse.com/img/uns-point-to-point-teL84klihq-650.jpeg" width="650" height="250" /></picture></p>
<p>For the same data to be transmitted to a new data consumer, the consumer needs
to make another request to obtain the data. This works great when you know what
data you need for building your solution, and if you know where to get it.</p>
<p>However, in manufacturing it’s not always possible to know up front who will
need your data. Some machines are built and placed years before another machine
would like to interact with the generated data. There might be many consumers
for the same data set. Lastly; consumers might not know when to fetch new data
points, and thus will try on a cycle or need another mechanism to understand if
new data is available.</p>
<p>This is why a hub and spoke model should be employed. For each data source or
data producer, a connection is made to a central hub; generally called a broker.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/uns-hub-fKoqSVxbXY-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/uns-hub-fKoqSVxbXY-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Unified Namespace Hub and Spokes communication" alt="Hub and spoke graphic" loading="lazy" decoding="async" src="https://flowfuse.com/img/uns-hub-fKoqSVxbXY-650.jpeg" width="650" height="325" /></picture></p>
<h2 id="structured-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/#structured-data"></a> Structured data</h2>
<p>When many producers are connected to many data consumers, but not directly,
the data producer needs to provide insight into what the events can be and will
contain. It cannot, nor should even if it could, tailor the event’s data structure
for a consumer so there’s decoupling on an architecture level. Structured data
makes information, without structure the consumer receives mere bytes.</p>
<p>This means that a schema for each event should be created and maintained. A
schema which both the producer and consumer can read and validate each event
against. As such a common Schema Definition Language (SDL) is chosen to provide
clarity of how the data is structured, how it can be parsed, and in some cases
also what it means for the developer.</p>
<h2 id="how-node-red-fits-in" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/#how-node-red-fits-in"></a> How Node-RED Fits In</h2>
<p>Node-RED excels in implementing a Unified Namespace with its flexible and powerful capabilities. It can act as both a central hub and a data consumer within a hub-and-spokes model, simplifying the integration of various data sources and consumers. By leveraging Node-RED's extensive library of nodes and its easy-to-use flow-based programming interface, organizations can efficiently manage data ingestion, transformation, and distribution.</p>
<p>Node-RED also supports structured data through its support for JSON, XML, and other standard formats, allowing for clear and consistent data schemas. With its built-in nodes for MQTT, HTTP, and other protocols, Node-RED can seamlessly integrate with existing systems, enabling real-time data exchange and visualization. This makes it an ideal tool for operationalizing the Unified Namespace, ensuring that data flows efficiently and is readily available to all relevant stakeholders.</p>
<h3 id="how-flowfuse-can-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/introduction-to-unified-namespace/#how-flowfuse-can-help"></a> How FlowFuse Can Help</h3>
<p>While Node-RED is highly effective for implementing UNS, managing and deploying it can be complex. FlowFuse provides a unified platform that simplifies deployment with one-click operations, secure management, and scalable Node-RED applications. It also includes features that enhance collaboration, alongside offering centralized management of all Node-RED instances to ensure streamlined operations and increased efficiency.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and experience FlowFuse's features</strong></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/Run Node-RED as a service on WindowsStep by step guide to run FlowFuse device agent as a Windows service2023-12-18T00:00:00ZSteve McLaughlinRob Marcer<p>FlowFuse's device agent allows you to manage and run your Node-RED instances on
your own hardware such as a Raspberry Pi or Windows computer. This can be very useful where an
application you've written needs to run flows with direct access to hardware sensors.</p>
<p>In this article, we're going to explain the steps to configure our device agent to run as a service in Windows
using the <a href="https://nssm.cc/">nssm</a> utility.</p>
<!--more-->
<h2 id="why-run-the-device-agent-as-a-service%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#why-run-the-device-agent-as-a-service%3F"></a> Why run the device agent as a service?</h2>
<p>The standard process for running FlowFuse's device agent is to start it on the
command line using the command <code>flowfuse-device-agent</code>. This works fine for testing
but for long-term installations it's useful to run the device agent as a service.
Once running as a service, the device agent will continue to run even if you
log off or the computer is restarted and no user is logged in.</p>
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#summary"></a> Summary</h2>
<p>The aim of this how-to is to install the FlowFuse device-agent as a service on a Windows computer.</p>
<p>There will be two main parts to this:</p>
<ol>
<li>Install the device-agent</li>
<li>Setup the device-agent to run as a Windows service</li>
</ol>
<p>Additionally, two user accounts will be needed for this configuration:</p>
<ol>
<li>A <strong>user</strong> account that will be used to run the device-agent (typically, non-admin account)</li>
<li>An <strong>admin</strong> account that can run elevated commands and will be used to setup the service</li>
</ol>
<p>We will create a directory for the device-agent files and set the permissions on that directory so that the <strong>user</strong> account can read and write files in that directory.
<em>This will be <code>c:\opt\flowfuse-device</code></em></p>
<p>To make the device-agent run as a service, we will (in this example), use <a href="https://nssm.cc/">nssm</a> but you are free to choose an alternative tool to run the device agent as a service.</p>
<p>Finally, we set the service to run under the <strong>service</strong> account.</p>
<p><em>NOTE: The instructions in this how to were written on <strong>Windows 11 Pro 22H2</strong></em></p>
<h3 id="tip%3A-using-domain-accounts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-using-domain-accounts"></a> TIP: Using domain accounts</h3>
<p>If the account is a domain account, append the domain name to the <strong>user</strong> e.g. <code>user@domain</code> whenever the <strong>user</strong> name is used in the instructions below.</p>
<h3 id="tip%3A-launching-an-elevated-command-prompt-window-(e.g.-as-the-admin-user)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-command-prompt-window-(e.g.-as-the-admin-user)"></a> TIP: Launching an elevated command prompt window (e.g. as the admin user)</h3>
<div style="position: relative" id="code-container-70">
<pre class="language-bash"><code id="code-70" class="language-bash">powershell <span class="token parameter variable">-Command</span> "Start-Process <span class="token string">'cmd'</span> <span class="token parameter variable">-Verb</span> runAs</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-70" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="tip%3A-launching-an-elevated-powershell-prompt-window-(e.g.-as-the-admin-user)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-powershell-prompt-window-(e.g.-as-the-admin-user)"></a> TIP: Launching an elevated powershell prompt window (e.g. as the admin user)</h3>
<div style="position: relative" id="code-container-74">
<pre class="language-bash"><code id="code-74" class="language-bash">powershell <span class="token parameter variable">-Command</span> "Start-Process <span class="token string">'powershell'</span> <span class="token parameter variable">-Verb</span> runAs</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-74" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="pre-requisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#pre-requisites"></a> Pre-requisites</h2>
<h3 id="install-node.js" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#install-node.js"></a> Install Node.js</h3>
<p>The device-agent requires Node.js to be installed. You can download the latest version from https://nodejs.org/en/download/.</p>
<p>It is recommended to install the LTS version and to check the "Automatically install the necessary tools" option. This is especially important if you intend on using any nodes that require native modules (like serialport).</p>
<h3 id="create-a-new-windows-user" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#create-a-new-windows-user"></a> Create a New Windows User</h3>
<p>If you need to create a new <strong>user</strong> account follow these <a href="https://support.microsoft.com/en-us/windows/create-a-local-user-or-administrator-account-in-windows-20de74e0-ac7f-3502-a866-32915af2a34d#:~:text=Select%20Start%20%3E%20Settings%20%3E%20Accounts%20and,other%20user%2C%20select%20Add%20account.">instructions</a>.</p>
<h2 id="prepare-the-device-agent-files-directory" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#prepare-the-device-agent-files-directory"></a> Prepare the device-agent files directory</h2>
<p>As the admin user, open an <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-command-prompt-window-(e.g.-as-the-admin-user)">elevated</a> command prompt, create the files directory and setup access permissions.</p>
<div style="position: relative" id="code-container-99">
<pre class="language-bash"><code id="code-99" class="language-bash"><span class="token comment"># In an elevated command prompt</span><br /><span class="token function">mkdir</span> c:<span class="token punctuation">\</span>opt<br /><span class="token function">mkdir</span> c:<span class="token punctuation">\</span>opt<span class="token punctuation">\</span>flowfuse-device<br /><span class="token comment"># grant full access to the service account that will run the device-agent</span><br />icacls c:<span class="token punctuation">\</span>opt<span class="token punctuation">\</span>flowfuse-device /grant <span class="token string">"user"</span>:F /T</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-99" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><em>where <code>"user"</code> is the service account (not the admin account)</em></p>
<h2 id="install-nssm" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#install-nssm"></a> Install nssm</h2>
<p><code>nssm</code> can simply be downloaded and executed from any path.
We will download it to the <code>c:\opt</code> directory, extract the files and copy the 64 bit version to the current directory.</p>
<h3 id="cmd-version-elevated-command-prompt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#cmd-version-elevated-command-prompt"></a> <code>cmd</code> version <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-command-prompt-window-(e.g.-as-the-admin-user)">elevated</a> command prompt</h3>
<div style="position: relative" id="code-container-112">
<pre class="language-bash"><code id="code-112" class="language-bash"><span class="token comment"># starting in the device-agent files directory</span><br /><span class="token builtin class-name">cd</span> c:<span class="token punctuation">\</span>opt<br /><span class="token comment"># download the nssm zip file</span><br /><span class="token function">curl</span> <span class="token parameter variable">-LJO</span> https://nssm.cc/release/nssm-2.24.zip<br /><span class="token comment"># extract the files</span><br /><span class="token function">tar</span> <span class="token parameter variable">-xf</span> nssm-2.24.zip<br /><span class="token comment"># copy the 64 bit version to the current directory</span><br />copy nssm-2.24<span class="token punctuation">\</span>win64<span class="token punctuation">\</span>nssm.exe <span class="token builtin class-name">.</span><br /><span class="token comment"># clean up</span><br />del nssm-2.24.zip<br /><span class="token function">rmdir</span> /s /q nssm-2.24</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-112" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="powershell-version-elevated-powershell-prompt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#powershell-version-elevated-powershell-prompt"></a> <code>powershell</code> version <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-powershell-prompt-window-(e.g.-as-the-admin-user)">elevated</a> powershell prompt</h3>
<p>If you don't have <code>cURL</code> installed, then powershell can be used to download the file. Here is how to do it:</p>
<div style="position: relative" id="code-container-119">
<pre class="language-powershell"><code id="code-119" class="language-powershell"><span class="token comment"># starting in the device-agent files directory</span><br />cd c:\opt<br /><span class="token comment"># download the nssm zip file</span><br /><span class="token function">Invoke-WebRequest</span> <span class="token operator">-</span>Uri https:<span class="token operator">/</span><span class="token operator">/</span>nssm<span class="token punctuation">.</span>cc/release/nssm-2<span class="token punctuation">.</span>24<span class="token punctuation">.</span>zip <span class="token operator">-</span>OutFile nssm-2<span class="token punctuation">.</span>24<span class="token punctuation">.</span>zip<br /><span class="token comment"># extract the files</span><br /><span class="token function">Expand-Archive</span> <span class="token operator">-</span>Path nssm-2<span class="token punctuation">.</span>24<span class="token punctuation">.</span>zip <span class="token punctuation">.</span><br /><span class="token comment"># copy the 64 bit version to the current directory</span><br /><span class="token function">Copy-Item</span> <span class="token operator">-</span>Path <span class="token punctuation">.</span>\nssm-2<span class="token punctuation">.</span>24\win64\nssm<span class="token punctuation">.</span>exe <span class="token operator">-</span>Destination <span class="token punctuation">.</span><br /><span class="token comment"># clean up</span><br /><span class="token function">Remove-Item</span> <span class="token operator">-</span>Path nssm-2<span class="token punctuation">.</span>24<span class="token punctuation">.</span>zip<br /><span class="token function">Remove-Item</span> <span class="token operator">-</span>Path nssm-2<span class="token punctuation">.</span>24 <span class="token operator">-</span>Recurse</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-119" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="manual-download" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#manual-download"></a> Manual download</h3>
<p>If you prefer, you can download the nssm zip file manually from <a href="https://nssm.cc/release/nssm-2.24.zip">https://nssm.cc/release/nssm-2.24.zip</a> and extract the files to the <code>c:\opt</code> directory. Then copy the 64 bit version to the current directory.</p>
<p>Ultimately, you should end up with a file named <code>nssm.exe</code> in the <code>c:\opt\</code> directory.</p>
<h2 id="install-and-configure-the-device-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#install-and-configure-the-device-agent"></a> Install and configure the device-agent</h2>
<p>As the <strong>service</strong> account, to do so open a command prompt window and run the following and authenticate:</p>
<div style="position: relative" id="code-container-135">
<pre class="language-bash"><code id="code-135" class="language-bash">runas /user:<span class="token punctuation">{</span>serviceuser<span class="token punctuation">}</span> cmd<br /><span class="token comment"># e.g. runas /user:winserv cmd</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-135" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><em>where <code>{serviceuser}</code> is the service account (not the admin account)</em></p>
<h3 id="check-the-users-npm-global-path-is-set-in-the-users-environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#check-the-users-npm-global-path-is-set-in-the-users-environment-variables"></a> Check the users npm global path is set in the Users Environment Variables</h3>
<p>NOTE: The recommended flowfuse-device-agent instructions will result in the flowfuse-device-agent being installed in the NPM global directory. And the instructions to launch the device-agent expect the NPM global directory to be in your user path. This section will instruct you to a) find the NPM global path, then b) check the user’s path setting and, if necessary c) add the NPM global path to your user path.</p>
<p>First, make a note of the path currently set for npm global. You can do this by running the following command:</p>
<div style="position: relative" id="code-container-148">
<pre class="language-bash"><code id="code-148" class="language-bash"><span class="token function">npm</span> config get prefix</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-148" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Next, ensure the <code>Path</code> Variable under the "User variables for <em>user</em>" contains the npm global path that we obtained in the previous step.
Use the below command, to check the user’s <code>Path</code> setting. If it is not present, edit the path to include it.</p>
<div style="position: relative" id="code-container-152">
<pre class="language-bash"><code id="code-152" class="language-bash"><span class="token comment"># This commands opens the environment variables editor, </span><br /><span class="token comment"># look for the "Path" variable under "User variables for user",</span><br /><span class="token comment"># and ensure it contains the npm global path</span><br />rundll32 sysdm.cpl,EditEnvironmentVariables</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-152" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>If you did have to add the npm path to the users <code>Path</code> variable, you will need to <strong>restart</strong> the command prompt for the change to take effect and relogin as <strong>user</strong>.</p>
<h3 id="install-the-device-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#install-the-device-agent"></a> Install the device agent</h3>
<p>Note: you may have already installed the device-agent, however, <strong>we strongly recommend</strong> you do this step again as the service account and ensure that account has the latest version.</p>
<div style="position: relative" id="code-container-162">
<pre class="language-bash"><code id="code-162" class="language-bash"><span class="token function">npm</span> i <span class="token parameter variable">-g</span> @flowfuse/device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-162" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="link-the-device-agent-to-your-flowfuse-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#link-the-device-agent-to-your-flowfuse-team"></a> Link the device-agent to your flowfuse team</h3>
<p>First, we must run the device-agent and link it to our FlowFuse team. This will generate a "device configuration" details that we will use to configure the device-agent.
Below is how to run the device-agent with the UI enabled. This will allow you to configure the device-agent via its web UI.</p>
<div style="position: relative" id="code-container-169">
<pre class="language-bash"><code id="code-169" class="language-bash">flowfuse-device-agent <span class="token parameter variable">--ui</span> --ui-port <span class="token number">8080</span> --ui-user admin --ui-pass admin <span class="token parameter variable">-d</span> c:<span class="token punctuation">\</span>opt<span class="token punctuation">\</span>flowfuse-device <span class="token parameter variable">-p</span> <span class="token number">1880</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-169" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The device-agent will now be running and you can access the UI at <a href="http://127.0.0.1:8080/">http://127.0.0.1:8080</a> with the user and password both "admin" (you can change these in the command line if required).
<em>NOTE: These credentials are temporary and only valid during the device setup</em></p>
<p>Proceed to configure the device-agent and link it to your flowfuse team. Full instructions can be found <a href="https://flowfuse.com/docs/device-agent/register/">here</a>.
Once you have linked the device-agent to your team, you can stop it by pressing <code>ctrl+c</code> in the command prompt window.</p>
<h2 id="create-the-device-agent-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#create-the-device-agent-service"></a> Create the device-agent service</h2>
<p>As the admin user, open an elevated command prompt see <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#tip%3A-launching-an-elevated-command-prompt-window-(e.g.-as-the-admin-user)">TIP</a> above</p>
<h3 id="install-device-agent-as-a-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#install-device-agent-as-a-service"></a> Install device-agent as a service</h3>
<div style="position: relative" id="code-container-185">
<pre class="language-bash"><code id="code-185" class="language-bash"><span class="token builtin class-name">cd</span> c:<span class="token punctuation">\</span>opt<br />.<span class="token punctuation">\</span>nssm.exe <span class="token function">install</span> flowfuse-device-agent <span class="token string">"flowfuse-device-agent.cmd"</span><br />.<span class="token punctuation">\</span>nssm.exe <span class="token builtin class-name">set</span> flowfuse-device-agent AppDirectory <span class="token string">"c:\opt<span class="token entity" title="\f">\f</span>lowfuse-device"</span><br />.<span class="token punctuation">\</span>nssm.exe <span class="token builtin class-name">set</span> flowfuse-device-agent Description <span class="token string">"FlowFuse Device Agent"</span><br /><span class="token comment"># set the AppParameters (cli options) to tell the agent where its home directory is</span><br /><span class="token comment"># in our case, this is c:\opt\flowfuse-device and is set with the -d option</span><br />.<span class="token punctuation">\</span>nssm.exe <span class="token builtin class-name">set</span> flowfuse-device-agent AppParameters <span class="token string">"-d c:\opt<span class="token entity" title="\f">\f</span>lowfuse-device -p 1880"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-185" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="check-the-service-is-installed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#check-the-service-is-installed"></a> Check the service is installed</h3>
<p>Run the following command to check the service is installed:</p>
<div style="position: relative" id="code-container-192">
<pre class="language-bash"><code id="code-192" class="language-bash">services.msc</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-192" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>(look for a service named `flowfuse-device-agent').</p>
<p>Alternatively, you can use the <code>sc</code> command:</p>
<div style="position: relative" id="code-container-199">
<pre class="language-bash"><code id="code-199" class="language-bash">sc query flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-199" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="set-the-user-account-that-will-run-the-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#set-the-user-account-that-will-run-the-service"></a> Set the user account that will run the service</h3>
<p>Some things are easier to edit in the UI, so we will edit the service via the NSSM UI.</p>
<div style="position: relative" id="code-container-206">
<pre class="language-bash"><code id="code-206" class="language-bash">nssm edit flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-206" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nssm_service_editor-e6Wml3xiQM-431.avif 431w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nssm_service_editor-e6Wml3xiQM-431.webp 431w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="nssm editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/nssm_service_editor-e6Wml3xiQM-431.jpeg" width="431" height="232" /></picture></p>
<p>In the UI, you can edit the service name, description, startup type, etc.
The most important thing to check is the <code>Application</code> tab. This includes the path to the flowfuse-device-agent.cmd and its arguments.
Select the "Log on" tab, select "This account" and enter the service account name and password that will run the device-agent.
Click the "Edit Service" button to save the changes.</p>
<p>Now you have a service that will run the device-agent as the <strong>service</strong> account 🎉</p>
<h3 id="controlling-the-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#controlling-the-service"></a> Controlling the service</h3>
<p>You can start the service with the command:</p>
<div style="position: relative" id="code-container-222">
<pre class="language-bash"><code id="code-222" class="language-bash">sc start flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-222" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>You can check the current status with the command:</p>
<div style="position: relative" id="code-container-226">
<pre class="language-bash"><code id="code-226" class="language-bash">sc query flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-226" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>You can stop the service with the command:</p>
<div style="position: relative" id="code-container-230">
<pre class="language-bash"><code id="code-230" class="language-bash">sc stop flowfuse-device-agent</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-230" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="further-reading" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/device-agent-as-a-windows-service/#further-reading"></a> Further reading</h3>
<p>If you'd like to learn about windows services via the <code>sc</code> command you can access
the help text by running <code>sc</code> from a command prompt.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/Building a Custom Video Player in Dashboard 2.0We've just released the latest version of Dashboard 2.0, with a fully featured UI Templates node which now allows for full definition of a Vue component, external JS dependencies and CSS.2023-12-07T00:00:00ZJoe Pavitt<p>Dashboard 2.0 just got <em>a lot</em> more powerful with our new updates to the <code>ui-template</code> node. New features added to the node include:</p>
<ul>
<li>Support for a full Vue component to be defined using the VueJS Options API.</li>
<li>Running of raw JavaScript within <code><script /></code> tags</li>
<li>Loading of external dependencies through <code><script /></code> tags</li>
</ul>
<!--more-->
<p>In this article we're going to deepdive into an example of how you can use this new functionality to build a custom video player.</p>
<p>We're going to aim for 3 key features:</p>
<ol>
<li>Emit events into Node-RED when a user plays/pauses the video</li>
<li>Allow for the video to be played/paused from within Node-RED</li>
<li>Allow the user to seek to a specific point in the video from within Node-RED</li>
</ol>
<div style="background-color: #fff4b9; border:1px solid #ffc400; color: #a27110; padding: 12px; border-radius: 6px; font-style: italic;">Reminder: all new releases of Dashboard are now under the <code style="background-color: transparent;">@flowfuse</code> namespace, so you'll need to update to use <code style="background-color: transparent;">@flowfuse/node-red-dashboard</code>, and not <code style="background-color: transparent;">@flowforge</code>.</div>
<h2 id="building-a-vue-component" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#building-a-vue-component"></a> Building a Vue Component</h2>
<p>With Dashboard 2.0, we switched over our underlying front-end framework to VueJS. We're aware that not everyone coming into Dashboard 2.0 will be familiar with VueJS.</p>
<p>We have a more detailed guide <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#building-full-vue-components">here</a>, but we'll also give a quick overview of the elements from Vue "component" that we'll use here:</p>
<div style="position: relative" id="code-container-54">
<pre class="language-html"><code id="code-54" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token comment"><!-- Our HTML content will go here --></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'MyComponent'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// JS methods we want to use across our component will go here</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Code we want to run when our component is loaded will go here</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">unmounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// Code we want to run when our component is unloaded will go here</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><br /><br /><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br /> <span class="token comment">/* We can define custom CSS here too */</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>style</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-54" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Some quick gotchas to note:</p>
<ul>
<li><code><div>{{ msg }}</div></code> - is an example of how you render variables into the HTML.</li>
<li><code><div v-if="myVar"></div></code> - lets you conditionally show/hide content based on a variable.</li>
<li><code><div v-for="item in items"></div></code> - lets you loop over an array of items and render them into the HTML.</li>
<li><code><div @click="myMethod"></div></code> - lets you bind a method to an event, in this case, when the user clicks on the div.</li>
<li><code><div :class="{ 'my-class': isActive }"></div></code> - <code>:</code> is a way to define a "bound" property. In this case, the class <code>my-class</code> will be applied when <code>isActive</code> is true.</li>
<li><code>console.log(this.myVar)</code> - when you're writing code inside the <code><script /></code> tags, you can access Component variables and methods using <code>this</code>.</li>
</ul>
<h3 id="built-in-extras" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#built-in-extras"></a> Built-in Extras</h3>
<p>In addition to building a component from scratch, we'll also utilize some built-in features of <code>ui-template</code> too. These will be:</p>
<ul>
<li><strong>Variables:</strong>
<ul>
<li><code>id</code> - The unique ID for this node in Node-RED</li>
<li><code>msg</code> - The message that was most recently received into the node</li>
<li><code>$socket</code> - The underlying SocketIO connection to Node-RED. Use this to listen to any incoming events, and send new ones back.</li>
</ul>
</li>
<li><strong>Functions:</strong>
<ul>
<li><code>send(payload)</code> - Send a message back to Node-RED</li>
</ul>
</li>
</ul>
<p>As above, we have more detailed documentation on these features <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#built-in-functionality">here</a>.</p>
<h2 id="building-the-video-player" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#building-the-video-player"></a> Building the Video Player</h2>
<h3 id="defining-the-content-(html)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#defining-the-content-(html)"></a> Defining the Content (HTML)</h3>
<p>We're going to start by adding a basic HTML video player:</p>
<div style="position: relative" id="code-container-144">
<pre class="language-html"><code id="code-144" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>video</span> <span class="token attr-name">ref</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my-video<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">width</span><span class="token punctuation">:</span> 100%</span><span class="token punctuation">"</span></span></span> <span class="token attr-name">controls</span> <span class="token attr-name">@play</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>onPlay<span class="token punctuation">"</span></span> <span class="token attr-name">@pause</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>onPause<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>video/mp4<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br /> Your browser does not support the video tag.<br /> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>video</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-144" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>A few things of importance to note here:</p>
<ul>
<li><code>ref</code> is Vue's replacement for <code>document.getElementById()</code>. This is copied to each instance of the component, meaning we can call <code>this.$refs['my-video']</code> to access the video element, and this doesn't break when duplicating the widget multiple times in Dashboard.</li>
<li><code>style=""</code> is required here to ensure the video fills the group/wrapper that it is contained within.</li>
<li><code>@play=</code> is Vue's way of binding onto the standard <code>onplay</code> event listener available on HTML video players. We'll define the <code>onPlay</code> method in the next section.</li>
<li><code>@pause=</code> is our event listener for when the video is paused by the user. As with <code>onPlay</code>, we'll define this shortly.</li>
</ul>
<p>With <em>just</em> the above defined, we end up with a standard video player rendered:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-video-1-OaL6U2jVtK-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-video-1-OaL6U2jVtK-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="HTML5 Video Player rendered in Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-video-1-OaL6U2jVtK-650.jpeg" width="650" height="386" /></picture></p>
<h3 id="defining-the-behaviors-(vuejs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#defining-the-behaviors-(vuejs)"></a> Defining the Behaviors (VueJS)</h3>
<p>Now we begin to build our Vue component. Referring back to our earlier set of features, we'll tackle these one at a time.</p>
<h4 id="1.-emitting-events-to-node-red-on-play%2Fpause" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#1.-emitting-events-to-node-red-on-play%2Fpause"></a> 1. Emitting Events to Node-RED on Play/Pause</h4>
<p>We can use <code>methods</code> to define our <code>onPlay</code> and <code>onPause</code> functions that are called <code>@play</code>/<code>@pause</code> respectively.</p>
<div style="position: relative" id="code-container-188">
<pre class="language-html"><code id="code-188" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'MyVideoPlayer'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token function">capture</span> <span class="token punctuation">(</span><span class="token parameter">eventType</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// let's define our own function that can be called onPlay/onPause</span><br /> <span class="token comment">// this prevents duplicated code across the two methods</span><br /><br /> <span class="token comment">// get the Video's DOM element</span><br /> <span class="token keyword">const</span> video <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$refs<span class="token punctuation">[</span><span class="token string">'my-video'</span><span class="token punctuation">]</span><br /><br /> <span class="token comment">// send a msg to Node-RED using built-in "send" fcn</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token comment">// specify which action is taking place</span><br /> <span class="token literal-property property">event</span><span class="token operator">:</span> eventType<span class="token punctuation">,</span><br /> <span class="token comment">// use Vue's $refs to get the video's currentTime</span><br /> <span class="token literal-property property">time</span><span class="token operator">:</span> video<span class="token punctuation">.</span>currentTime<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">onPlay</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">capture</span><span class="token punctuation">(</span><span class="token string">'play'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">onPause</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">capture</span><span class="token punctuation">(</span><span class="token string">'pause'</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-188" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>With this functionality in place, we can wire the <code>ui-template</code> node to a <code>debug</code> node, and see the following when we play/pause the video:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-video-2-o3x7uYRRNS-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-video-2-o3x7uYRRNS-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Example debug output when our custom build video player is played/paused" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-video-2-o3x7uYRRNS-650.jpeg" width="650" height="290" /></picture></p>
<h4 id="2.-remote-control-of-play%2Fpause-from-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#2.-remote-control-of-play%2Fpause-from-node-red"></a> 2. Remote control of play/pause from Node-RED</h4>
<p>We can use the built-in <code>$socket</code> variable to listen for incoming events from Node-RED. When Dashboard 2.0's nodes receive a <code>msg</code> inside Node-RED, they send a <code>msg-input:<node-id></code> event to the Dashboard client. We can listen for this event and then call the <code>play()</code> and <code>pause()</code> methods on the video element, depending on any properties of that message, in this case, the <code>msg.payload.event</code> value.</p>
<div style="position: relative" id="code-container-201">
<pre class="language-html"><code id="code-201" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'MyVideoPlayer'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// listen for incoming msg's from Node-RED</span><br /> <span class="token comment">// note our topic is "msg-input" + the node's unique ID</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>$socket<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'msg-input:'</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">msg</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// get the Video's DOM element</span><br /> <span class="token keyword">const</span> video <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>$refs<span class="token punctuation">[</span><span class="token string">'my-video'</span><span class="token punctuation">]</span><br /><br /> <span class="token comment">// if the event is "play", call the video's play() method</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token operator">?.</span>event <span class="token operator">===</span> <span class="token string">'play'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> video<span class="token punctuation">.</span><span class="token function">play</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><br /> <span class="token comment">// if the event is "pause", call the video's pause() method</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token operator">?.</span>event <span class="token operator">===</span> <span class="token string">'pause'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> video<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">unmounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// make sure we remove our listeners when the widget is destroyed</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>$socket<span class="token punctuation">.</span><span class="token function">off</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">msg-input:</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-201" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="3.-seeking-to-a-specific-point-in-the-video-from-within-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#3.-seeking-to-a-specific-point-in-the-video-from-within-node-red"></a> 3. Seeking to a specific point in the video from within Node-RED</h4>
<p>With the <code>on('msg-input')</code> listener in place, we can now extend our handler to handle seeking to a specific point in the video.</p>
<div style="position: relative" id="code-container-208">
<pre class="language-html"><code id="code-208" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br /><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'MyVideoPlayer'</span><span class="token punctuation">,</span><br /> <span class="token literal-property property">methods</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>$socket<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'msg-input:'</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">msg</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> <span class="token comment">// ... other handlers</span><br /><br /> <span class="token comment">// if the event is "seek", call the video's currentTime() method</span><br /> <span class="token keyword">if</span> <span class="token punctuation">(</span>msg<span class="token punctuation">.</span>payload<span class="token operator">?.</span>event <span class="token operator">===</span> <span class="token string">'seek'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> video<span class="token punctuation">.</span>currentTime <span class="token operator">=</span> msg<span class="token punctuation">.</span>payload<span class="token punctuation">.</span>currentTime<br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">unmounted</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token comment">// ...</span><br /> <span class="token punctuation">}</span><br /><span class="token punctuation">}</span><br /></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-208" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>and with that, we now have a Dashboard 2.0 widget to display a video, that can be controlled from Node-RED, and logs details of user activity back into Node-RED.</p>
<p>Other features available with the UI Template are detailed in the online documentation, and include:</p>
<ul>
<li><a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#loading-external-dependencies">Loading External Dependencies</a></li>
<li><a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html#writing-raw-javascript">Running raw JavaScript</a></li>
</ul>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/dashboard-0-10-0/#follow-our-progress"></a> Follow our Progress</h2>
<p>You can also read the more comprehensive release notes for <code>v0.10.0</code> release here:</p>
<ul>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.10.0">0.10.0 Release Notes</a></li>
</ul>
<p>As always, thanks for reading and your interest in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/Beyond Automation - AI Use Cases that are shaping the next manufacturing frontierIn which AI-powered capabilities should one invest to bring about transformative changes in the manufacturing environment?2023-12-04T00:00:00Z<p>Are we standing on the brink of a Fifth Industrial Revolution? The manufacturing industry has been in a state of flux for some time, with the rise of automation and digital transforming the way factories operate. But today, we are witnessing something even more profound: AI is pushing manufacturing to a whole new level. Some have even referred to it as “the fifth industrial revolution” due to its potential for disruption.</p>
<!--more-->
<p>But the question lingers for many plant managers and decision makers: in which AI-powered capabilities should one invest to bring about transformative changes in the manufacturing environment?</p>
<p>As we navigate this question, I want to focus on three AI uses that are not only ripe for investment but also pivotal in driving manufacturing success in this competitive market.</p>
<h2 id="empowering-citizen-developer-strategy-with-ai" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/#empowering-citizen-developer-strategy-with-ai"></a> Empowering Citizen Developer Strategy with AI</h2>
<p>The concept of citizen development stands as one of the most significant fields in my opinion, a sentiment I've detailed in my <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/">previous article</a> about Citizen Developers. This approach is revolutionizing the manner in which applications are crafted and deployed across various industries. By empowering individuals—irrespective of their coding knowledge—to create applications, AI is dramatically hastening this process.</p>
<p>Investing in AI capabilities that bolster your citizen developer strategy can fast-track application development, offering intuitive, template-driven platforms that employ AI to navigate users through the creation process. As we've seen over recent months and years, AI can significantly assist in code generation, thereby granting your citizen developers an even smoother initiation into application development.</p>
<p>An excellent instance of this is the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/">article and Node-RED Node</a> describing the potential for integrating Node-RED with ChatGPT to assist you in building applications. This integration highlights the practical, user-friendly solutions made possible through AI, making the realm of app development accessible to a broader range of innovators.</p>
<h2 id="refining-warehouse-management-through-ai-driven-demand-forecasting" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/#refining-warehouse-management-through-ai-driven-demand-forecasting"></a> Refining Warehouse Management through AI-Driven Demand Forecasting</h2>
<p>In an era marked by complexities in supply chains and customer demand, AI's role in warehouse management becomes a game-changer. AI algorithms analyze historical data and market trends to predict future demand with astonishing accuracy, a step beyond traditional forecasting methods.</p>
<p>For decision makers, investing in AI for demand forecasting means significantly minimizing overproduction or stock outs, optimizing inventory levels, and improving customer satisfaction. The advanced analytics offered by AI not only predict what products are in demand but also when and where they are needed, thereby facilitating strategic planning and resource allocation.</p>
<h2 id="elevating-predictive-maintenance-and-quality-control" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/#elevating-predictive-maintenance-and-quality-control"></a> Elevating Predictive Maintenance and Quality Control</h2>
<p>Unplanned downtime and quality inconsistencies are two of the biggest profit drains in manufacturing. AI's predictive capabilities are setting new standards in both maintenance and quality control protocols. By continuously monitoring equipment performance and production processes, AI can predict and identify machinery failures before they occur and detect quality deviations in real-time—allowing for immediate correction. Investing here means less downtime, reduced maintenance costs, improved product quality, and ultimately, an enhanced bottom line.</p>
<h2 id="your-digital-infrastructure-%26-architecture-is-key" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/12/ai-use-cases/#your-digital-infrastructure-%26-architecture-is-key"></a> Your Digital Infrastructure & Architecture is key</h2>
<p>While understanding where concrete Use Cases are is crucial, it’s equally important to ensure that your digital strategy and architecture can support and quickly adapt to these advanced AI implementations. See also <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/">my article</a> about the Unified Namespace. A flexible system that integrates a Unified Namespace is critical for seamless data exchange across various systems and applications. Moreover, fostering a citizen developer environment is fundamental in ensuring that these AI investments are maximally utilized, empowering your workforce to contribute actively to the company's innovation cycle.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/Deploying the FlowFuse Device Agent via BalenaUsing Balena.io to Deploy fleets of devices2023-11-24T00:00:00ZBen Hardill<p>As part of the FlowFuse Staff Summit this year in Barcelona we met up with Marc Pous from <a href="https://www.balena.io/">Balena Io</a>. Balena is a platform for managing fleets of Edge Devices and it felt like the perfect fit for deploying the FlowFuse Device Agent.</p>
<!--more-->
<p>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.</p>
<p>These Docker container are hosted on Balena's own container registry and are built by doing a git push to Balena's git server.</p>
<p>A GitHub repository with all the required files <a href="https://github.com/FlowFuse/balena-device-agent">has been published</a>, a one click deploy button to allow you to quickly try this out.</p>
<h2 id="building-flowfuse-device-agent-for-balena" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#building-flowfuse-device-agent-for-balena"></a> Building FlowFuse Device Agent for Balena</h2>
<p>We already build a FlowFuse Device Agent Docker container so it was pretty simple to modify the existing <code>Dockerfile</code> for Balena.</p>
<div style="position: relative" id="code-container-19">
<pre class="language-docker"><code id="code-19" class="language-docker"><span class="token instruction"><span class="token keyword">FROM</span> balenalib/%%BALENA_MACHINE_NAME%%-alpine-node</span><br /><br /><span class="token instruction"><span class="token keyword">RUN</span> mkdir /opt/flowfuse-device</span><br /><span class="token instruction"><span class="token keyword">RUN</span> npm install -g @flowfuse/device-agent</span><br /><br /><span class="token instruction"><span class="token keyword">COPY</span> entrypoint.sh /usr/src/entrypoint.sh</span><br /><br /><span class="token instruction"><span class="token keyword">ENTRYPOINT</span> [<span class="token string">"/usr/src/entrypoint.sh"</span>]</span><br /><br /><span class="token instruction"><span class="token keyword">CMD</span> [<span class="token string">"flowfuse-device-agent"</span>]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-19" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>There were 2 main changes from the default Device Agent <a href="https://github.com/FlowFuse/device-agent/blob/main/docker/Dockerfile"><code>Dockerfile</code></a></p>
<ol>
<li>
<p>Change the base image to Balena's image, this is because the <code>Dockerfile</code> 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)</p>
</li>
<li>
<p>Adding a custom <code>entrypoint.sh</code>. 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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#configuring-devices">below</a>)</p>
</li>
</ol>
<p>As well as the <code>Dockerfile</code> there is also a <code>docker-compose.yml</code> 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.</p>
<h2 id="configuring-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#configuring-devices"></a> Configuring Devices</h2>
<p>The FlowFuse Device agent can be configured in 2 ways.</p>
<ol>
<li>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#environment-variable">below</a>.</p>
</li>
<li>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#environment-variable">below</a>.
You can create a Provisioning Token file under the Team -> Settings page on the Devices tab.</p>
</li>
</ol>
<h3 id="environment-variable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/device-agent-balena/#environment-variable"></a> Environment Variable</h3>
<p>Because the <code>device.yml</code> file is multi line it needs to be base64 encoded, you can do this with the following</p>
<div style="position: relative" id="code-container-62">
<pre class="language-bash"><code id="code-62" class="language-bash">$ base64 <span class="token parameter variable">-w</span> <span class="token number">0</span> device.yml</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-62" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>You can then use the Balena console to create either a device specific or a fleet wide environment variable called <code>FF_DEVICE_YML</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/balena-env-var-RjtVPQn-so-650.avif 650w, https://flowfuse.com/img/balena-env-var-RjtVPQn-so-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/balena-env-var-RjtVPQn-so-650.webp 650w, https://flowfuse.com/img/balena-env-var-RjtVPQn-so-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/balena-env-var-RjtVPQn-so-650.jpeg 650w, https://flowfuse.com/img/balena-env-var-RjtVPQn-so-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="balena-env-var" loading="lazy" decoding="async" src="https://flowfuse.com/img/balena-env-var-RjtVPQn-so-650.jpeg" width="1300" height="206" /></picture></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-8-0/Overhauling the Dashboard 2.0 Build PipelineA month or so ago, we released the Third-Party widget support for Dashboard 2.0, but having seen the feedback, we missed the beat, so we've built it again!2023-11-23T00:00:00ZJoe Pavitt<p>As a developer, sometimes you have to hold up your hands and realise something you've spent two weeks building needs to be thrown away and restarted.</p>
<!--more-->
<p>Having shipped the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/">third-party widget support for Dashboard 2.0</a> in line with Dashboard 1.0's approach, we then had the <a href="https://github.com/FlowFuse/node-red-dashboard/issues/307">feedback</a> that the way Dashboard 1.0 did things really wasn't good, and asking us to consider re-building the process to make the developer experience of working with Dashboard 2.0 far more seamless.</p>
<p>So, that's exactly what we've done with the <code>0.8.0</code> release, amongst a few other things.</p>
<div style="background-color: #fff4b9; border:1px solid #ffc400; color: #a27110; padding: 12px; border-radius: 6px; font-style: italic;">Reminder: all new releases of Dashboard are now under the <code style="background-color: transparent;">@flowfuse</code> namespace, so you'll need to update to use <code style="background-color: transparent;">@flowfuse/node-red-dashboard</code>, and not <code style="background-color: transparent;">@flowforge</code>.</div>
<h2 id="migrating-our-build-pipeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-8-0/#migrating-our-build-pipeline"></a> Migrating our Build Pipeline</h2>
<p>Without getting <em>too</em> technical, as part of this work in supporting third-party widgets, we overhauled our build pipeline for Dashboard 2.0. This pipeline is responsible for taking our source code, and compiling it into a format that then gets deployed by Node-RED when running Dashboard.</p>
<p>Previously, we used <em><strong>Webpack</strong></em>, but now, we've switched over to <em><strong>Vite</strong></em>. This is a newer build tool, and is much faster than Webpack. It's also what we've now updated out <a href="https://github.com/FlowFuse/node-red-dashboard-2-ui-example">Example Node</a> to use too.</p>
<p>So now, when working with a third-party widget, Vite builds up all of your code, wraps it into a single <code>umd.js</code> file, and Node-RED then serves that file up for Dashboard 2.0 to load in.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-650.avif 650w, https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-650.webp 650w, https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-650.jpeg 650w, https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Vite Build Process" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-build-FyNBuCjiDS-650.jpeg" width="1300" height="758" /></picture></p>
<p>We've also re-written our <a href="https://dashboard.flowfuse.com/contributing/widgets/third-party.html">"Building Third Party Widgets"</a> guide to reflect this change.</p>
<h2 id="debugging-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-8-0/#debugging-dashboard"></a> Debugging Dashboard</h2>
<p>A new feature we've added in <code>0.8.0</code> is also for those developing Dashboard's core and third-party widgets. You can now navigate to <code>/dashboard/_debug</code> to explore the full configuration that Dashboard receives from Node-RED.</p>
<p>This is particularly useful when you're trying to debug why a widget isn't loading, showing the correct data, or generally isn't behaving as you expect.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/debug-view-HIAUMnVQB_-650.avif 650w, https://flowfuse.com/img/debug-view-HIAUMnVQB_-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/debug-view-HIAUMnVQB_-650.webp 650w, https://flowfuse.com/img/debug-view-HIAUMnVQB_-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/debug-view-HIAUMnVQB_-650.jpeg 650w, https://flowfuse.com/img/debug-view-HIAUMnVQB_-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Dashboard's Debug View" loading="lazy" decoding="async" src="https://flowfuse.com/img/debug-view-HIAUMnVQB_-650.jpeg" width="1300" height="1086" /></picture></p>
<p>Note in the above example, where we can see that the <code>ui-dropdown</code> has had it's options overriden by <code>msg.options</code> on injection.</p>
<p>You can read more about the debugging view <a href="https://dashboard.flowfuse.com/contributing/widgets/debugging.html">here</a></p>
<h2 id="what-else-is-new-in-0.8.0%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-8-0/#what-else-is-new-in-0.8.0%3F"></a> What else is new in 0.8.0?</h2>
<p>Whilst we focussed this article on the build pipeline overhaul, changes to third-party wdgets and debugging Dashboard, we did also squeeze quite a lot more into the 0.8.0 releases too with plenty other fixes and improvements:</p>
<ul>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/345">Dynamic setting of msg.options for UI Dropdown</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/346">"Date" type for UI Text Input</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/365">Finer grain controls of Text Input event emissions</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/367">Control over when UI Slider emits events</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/364">Improved documentation for Bar Charts</a></li>
</ul>
<p>You can also read the more comprehensive release notes for the release here:</p>
<ul>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.8.0">0.8.0 Release Notes</a></li>
</ul>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-8-0/#follow-our-progress"></a> Follow our Progress</h2>
<p>As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/Integrate with ChatGPT Assistants with Node-REDGet start quickly leveraging Flows utilizing ChatGPT Assistant2023-11-21T00:00:00Z<h2 id="introduction-to-the-world-of-gpts-and-ai-assistants" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/#introduction-to-the-world-of-gpts-and-ai-assistants"></a> Introduction to the World of GPTs and AI Assistants</h2>
<p>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.</p>
<p>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.</p>
<!--more-->
<p><a href="https://flows.nodered.org/flow/073548c276832e804f037f3212014e60">TLDR: Give me the Flows</a></p>
<h2 id="node-red-and-ai-assistants" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/#node-red-and-ai-assistants"></a> Node-RED and AI Assistants</h2>
<p>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.</p>
<h2 id="experience-the-integration-firsthand" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/#experience-the-integration-firsthand"></a> Experience the Integration Firsthand</h2>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ai-flows-gR89-H3X4R-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ai-flows-gR89-H3X4R-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="OpenAI Assistant integration on Node-RED" loading="lazy" decoding="async" src="https://flowfuse.com/img/ai-flows-gR89-H3X4R-650.jpeg" width="650" height="319" /></picture></p>
<ol>
<li>
<p><strong>Create Assistant</strong>: 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.</p>
</li>
<li>
<p><strong>List Assistants</strong>: 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.</p>
</li>
<li>
<p><strong>Delete Assistant</strong>: 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.</p>
</li>
<li>
<p><strong>Adjust Assistant Instructions and Models</strong>: 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.</p>
</li>
<li>
<p><strong>Create Thread and Run</strong>: 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.</p>
</li>
</ol>
<p>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 <a href="https://flows.nodered.org/flow/073548c276832e804f037f3212014e60">here.</a></p>
<h2 id="embracing-the-future-of-ai-and-automation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/ai-assistant/#embracing-the-future-of-ai-and-automation"></a> Embracing the Future of AI and Automation</h2>
<p>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.</p>
<p><em>Always consult with management before uploading company data to public services like ChatGPT.</em></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/chatgpt-gpt/Node-RED Builder a GPT (Alpha) by FlowFuseSpeed Up Flow Creation with Your Personal Assistant2023-11-15T00:00:00Z<p>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.</p>
<!--more-->
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p><a href="https://chat.openai.com/g/g-V5Kyn4omE-node-red-builder-by-flowfuse-v1-0-2">Access to the GPT - Node-RED builder by FlowFuse</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-2.0-user-tracking/Tracking Who Has Opened a DashboardUsing FlowFuse Authentication Audit Dashboard v2 Access2023-11-13T00:00:00ZBen Hardill<p>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.</p>
<!--more-->
<h2 id="flowfuse-authentication" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-2.0-user-tracking/#flowfuse-authentication"></a> FlowFuse Authentication</h2>
<p>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.</p>
<p>We even offer a specific RBAC 'viewer' role that just allows access to these endpoints but not the FlowFuse application.</p>
<p>FlowFuse authentication can be enabled from the Instance Settings page on the Security tab</p>
<p>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.</p>
<p>But if we include an element in the Dashboard loaded via a HTTP-in/HTTP-response node we gain access to details of the authenticated user.</p>
<h2 id="implementation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-2.0-user-tracking/#implementation"></a> Implementation</h2>
<p>First we will create a HTTP-in/HTTP-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.</p>
<p>The following flow snippet includes both the HTTP-in/HTTP-response nodes and a change node to set the <code>msg.payload</code> to the SVG content and to set the HTTP headers to include the correct mime type.</p>
<p>There is also a second change node which extracts the user information.</p>
<div id="nr-flow-130" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow130 = "\n[{\"id\":\"7f22dc81d8192d4d\",\"type\":\"http in\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"url\":\"/tracker\",\"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\":[]},{\"id\":\"7d36739c02cd04ec\",\"type\":\"change\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"<svg width=\\\"1\\\" height=\\\"1\\\"> <rect width=\\\"1\\\" height=\\\"1\\\" style=\\\"fill:rgb(255,255,255);stroke-width:3;stroke:rgb(0,0,0)\\\" /> Sorry, your browser does not support inline SVG.</svg>\",\"tot\":\"str\"},{\"t\":\"set\",\"p\":\"headers\",\"pt\":\"msg\",\"to\":\"{\\\"Content-Type\\\":\\\"image/svg+xml\\\"}\",\"tot\":\"json\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":420,\"y\":460,\"wires\":[[\"58fd30516a077e29\"]]},{\"id\":\"5f4647c97917cce1\",\"type\":\"change\",\"z\":\"98c8d7ea66149291\",\"name\":\"\",\"rules\":[{\"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\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow130.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-130') })</script>
<p>Next we need to add the SVG to the Dashboard, this can be done by adding a Template node with the following HTML content.</p>
<div style="position: relative" id="code-container-38">
<pre class="language-html"><code id="code-38" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><br /> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>object</span> <span class="token attr-name">data</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/tracker<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>object</span><span class="token punctuation">></span></span><br /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-38" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will load the image every time the Dashboard page loads and hence trigger the earlier flow allowing the user to be logged.</p>
<h2 id="linking-to-sso-users" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-2.0-user-tracking/#linking-to-sso-users"></a> Linking to SSO Users</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/Chart Improvements & Migrating to Dashboard 2.0With the 0.7.x Releases of Dashboard 2.0, we've made big improvements to Charts, generated a migration guide, and much more...2023-11-09T00:00:00ZJoe Pavitt<p>It's been a little while since we've done an update, since we last posted we've moved into the 0.7.x releases for Dashboard 2.0. With these we're making big strides in improving the UX for charting your data, as well as starting to focus on migration paths from Dashboard 1.0 to 2.0.</p>
<!--more-->
<h2 id="package-name-changes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#package-name-changes"></a> Package Name Changes</h2>
<p>Firstly a bit of news regarding the <code>npm</code> package we publish. Inline with our own <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/">company name change</a>, we've had to update Dashboard 2.0's npm package, and so, we've changed from <code>@flowforge/node-red-dashboard</code> to <code>@flowfuse/node-red-dashboard</code>.</p>
<p>In the short term, we'll be keeping @flowforge available on the Node-RED Palette Manager, but it will be removed soon, and the associated NPM Package will be put into a "deprecated" mode.</p>
<h3 id="npm-package-migration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#npm-package-migration"></a> NPM Package Migration</h3>
<p>Unfortunately, this migration from <code>@flowforge/</code> to <code>@flowfuse/</code> requires a little bit of manual work, and isn't as easy as just clicking "update" in the Node-RED Editor.</p>
<p>In any case, there is no need to update your flow in this migration, you'll just need to uninstall <code>@flowforge/node-red-dashboard</code> and install <code>@flowfuse/node-red-dashboard</code> instead.</p>
<h4 id="running-locally" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#running-locally"></a> Running Locally</h4>
<p>If you're running Node-RED locally, or in your own infrastructure, you'll need to manually uninstall the old package:</p>
<div style="position: relative" id="code-container-28">
<pre class="language-bash"><code id="code-28" class="language-bash"><span class="token function">npm</span> uninstall @flowforge/node-red-dashboard</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-28" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>and re-install the new one:</p>
<div style="position: relative" id="code-container-32">
<pre class="language-bash"><code id="code-32" class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> @flowfuse/node-red-dashboard</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-32" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h4 id="running-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#running-in-flowfuse"></a> Running in FlowFuse</h4>
<p>Navigate to your Instance > Settings > Palette, and then change the <code>@flowforge/node-red-dashboard</code> entry to <code>@flowfuse/node-red-dashboard</code> (the latest version as of this post is <code>0.7.2</code>).</p>
<p>Restart your instance, and the new package will automatically install.</p>
<h2 id="dashboard-1.0-to-2.0-migration-guide" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#dashboard-1.0-to-2.0-migration-guide"></a> Dashboard 1.0 to 2.0 Migration Guide</h2>
<p>As part of our mission to ensure a smooth transition from Dashboard 1.0 to Dashboard 2.0, we have published a first draft of a <a href="https://dashboard.flowfuse.com/user/migration.html">Migration Guide</a>.</p>
<p>As a starting point, we have comprehesively covered the Dashboard 1.0 widgets and their associated properties. We've then detailed which properties are already supported, which have partial support, and where appropriate, we do not support and <em>why</em> (most of the time, it's just because we haven't got round to it yet!)</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-650.avif 650w, https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-650.webp 650w, https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-650.jpeg 650w, https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Migration Guide Snippet" loading="lazy" decoding="async" src="https://flowfuse.com/img/migration-guide-snippet-MZJIpyqjWq-650.jpeg" width="1300" height="1128" /></picture></p>
<p>We fully appreciate that the migration path is not yet complete, and we that we are missing some features and properties, but please know that we are working hard to ensure that as many of the features from Dashboard 1.0 are available in Dashboard 2.0. We will be updating this guide as we progress.</p>
<p>We will also be adding "event" and "dynamic properties" sections to the guide, to detail how you can update and control elements via runtime messages (e.g. dynamically change the label of a button), and how this differs (if at all) from Dashboard 1.0.</p>
<p>Whilst we aren't quite there yet, this guide offers a comprehensive breakdown on our progress in backporting all of the properties from Dashboard 1.0</p>
<h3 id="automated-script" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#automated-script"></a> Automated Script</h3>
<p>An ambitious plan that we have is to also provide a <a href="https://github.com/FlowFuse/node-red-dashboard/issues/261">Migration Script</a>. Any feedback, ideas or concerns are most welcome as comments on the issue.</p>
<p>Whilst this will never provide 100% perfect migration, we hope to be able to provide a script that can be run against your flows to automatically convert as much as possible from Dashboard 1.0 to 2.0. In most cases, as you can see in the Migration guide, we match most properties 1:1, so this should do a lot of the heavy lifting for you.</p>
<h2 id="updates-to-ui-chart" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#updates-to-ui-chart"></a> Updates to UI Chart</h2>
<h3 id="key-mapping" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#key-mapping"></a> Key Mapping</h3>
<p>One of the core purposes of Node-RED Dashboard has always been to provide low-code access to charting your data. With the 0.7.x releases we've made some big improvements to the UI Chart node to make it easier to use and more powerful.</p>
<p>In Dashboard 1.0, it was common place to have to regularly re-format your own data into <code>{x, y}</code> structure to be chart-friendly. In <code>0.7.0</code> we've introduced the concept of <strong>key mapping</strong>, where you can specify which keys in your data object should be used for the x and y axes. This means you can now pass in data in the format you want to use, and the chart will do the rest.</p>
<p>For example, when rendering a chart of our weekly npm downloads, we have a data structure:</p>
<div style="position: relative" id="code-container-87">
<pre class="language-json"><code id="code-87" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"YYYY-MM-DD"</span><span class="token punctuation">,</span><br /> <span class="token property">"downloads"</span><span class="token operator">:</span> <span class="token number">128</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"YYYY-MM-DD"</span><span class="token punctuation">,</span><br /> <span class="token property">"downloads"</span><span class="token operator">:</span> <span class="token number">256</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"YYYY-MM-DD"</span><span class="token punctuation">,</span><br /> <span class="token property">"downloads"</span><span class="token operator">:</span> <span class="token number">512</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-87" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Rather than having to pipe this into a <code>function</code> node and re-map the properties to <code>{ x, y }</code>, we can now use the <code>ui-chart</code>'s key mapping properties:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-650.avif 650w, https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-650.webp 650w, https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-650.jpeg 650w, https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ui-chart-key-mapping" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-keymap-properties--ZCq9BowE9-650.jpeg" width="1300" height="184" /></picture></p>
<p>Resulting in the following chart:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-650.avif 650w, https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-650.webp 650w, https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-650.jpeg 650w, https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ui-chart-key-mapping" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-mapping-j8hAXD6G6Q-650.jpeg" width="1300" height="470" /></picture></p>
<h3 id="multiple-lines" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#multiple-lines"></a> Multiple Lines</h3>
<p>Note above, another new option has been added to define <em>"Series"</em>. In Dashboard 1.0 this was fixed as <code>msg.topic</code> at all times, and defined which line/series data points rendered too. Now, this is configurable, and can even be set as a <code>key:</code> type too, whereby each data point being treated individually, and grouped based on a given key/property.</p>
<p>Another great new feature here is the type <code>JSON</code> for this property. We can provide a <em>list</em> of series labels, and the chart will render each value from a single data point as separate lines.</p>
<p>For example, if we consider the data:</p>
<div style="position: relative" id="code-container-112">
<pre class="language-json"><code id="code-112" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"2023-10-23"</span><span class="token punctuation">,</span><br /> <span class="token property">"temperature"</span><span class="token operator">:</span> <span class="token number">28</span><span class="token punctuation">,</span><br /> <span class="token property">"humidity"</span><span class="token operator">:</span> <span class="token number">16</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"2023-10-24"</span><span class="token punctuation">,</span><br /> <span class="token property">"temperature"</span><span class="token operator">:</span> <span class="token number">26</span><span class="token punctuation">,</span><br /> <span class="token property">"humidity"</span><span class="token operator">:</span> <span class="token number">19</span><br /><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token property">"day"</span><span class="token operator">:</span> <span class="token string">"2023-10-25"</span><span class="token punctuation">,</span><br /> <span class="token property">"temperature"</span><span class="token operator">:</span> <span class="token number">27</span><span class="token punctuation">,</span><br /> <span class="token property">"humidity"</span><span class="token operator">:</span> <span class="token number">24</span><br /><span class="token punctuation">}</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-112" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>We can provide a series: <code>["temperature", "humidity"]</code> like so:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-650.avif 650w, https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-650.webp 650w, https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-650.jpeg 650w, https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ui-chart-key-mapping" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-series-property--BVAofVJL9-650.jpeg" width="1300" height="169" /></picture></p>
<p>Which would result in the following plot:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-650.avif 650w, https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-650.webp 650w, https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-650.jpeg 650w, https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ui-chart-key-mapping" loading="lazy" decoding="async" src="https://flowfuse.com/img/ui-chart-multipoint-FipKs1UdB_-650.jpeg" width="1300" height="461" /></picture></p>
<p>We appreciate this offers a new way of working with data in Dashboard, but hopefully, once you've tried it, you'll find it much easier to work with, and see the value it brings when working with your own data sets.</p>
<h2 id="what-else-is-new-in-0.7.x%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#what-else-is-new-in-0.7.x%3F"></a> What else is new in 0.7.x?</h2>
<p>Whilst we focussed this article on the migration paths and new UI Chart features, we did also squeeze quite a lot more into the 0.7.x releases too with plenty other fixes and improvements:</p>
<ul>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/279">Re-architecture of Server-side State Management</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/327">Y Axis Min/Max Options</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/320">"Focus" button for widgets added to Sidebar</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/310">No more "blue screen" and improved error reporting</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/pull/301">Better route handling</a></li>
</ul>
<p>You can also read the more comprehensive release notes for each release here:</p>
<ul>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.0">0.7.0 Release Notes</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.1">0.7.1 Release Notes</a></li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.7.2">0.7.2 Release Notes</a></li>
</ul>
<h2 id="follow-our-progress" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/dashboard-0-7/#follow-our-progress"></a> Follow our Progress</h2>
<p>As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/Community News November 2023Your monthly update for the FlowFuse and Node-RED communities2023-11-09T00:00:00Z<p>Welcome to the FlowFuse newsletter for November 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="flowfuse-team-summit---barcelona-by-grey" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#flowfuse-team-summit---barcelona-by-grey"></a> FlowFuse Team Summit - Barcelona by Grey</h2>
<p>The first week of November in Barcelona, the FlowFuse team embraced a blend of culture, collaboration, and creativity at our biannual summit. As a global remote-first company, these gatherings are key to nurturing the camaraderie and clear communication that fuels our work.</p>
<p>My first summit was a vibrant tableau of wit and good-natured sarcasm, a perfect fit for my sense of humor. But it wasn't just about the fun; we delved deep into aligning our strategies to streamline communication with the vast community that recognizes the value of Node-RED.</p>
<p>From segway tours through historic streets to culinary competitions, we bonded and built memories. During the day, we crafted go-to-market strategies, fortified our team dynamics through board games, and honed our sales approaches. The latest dashboard visualizations and product roadmaps were discussed, with our shared vision to simplify the complex for companies worldwide.</p>
<p><strong>“The easy things should be easy, and the hard things should be possible.”</strong></p>
<p>Our goal remains steadfast: to enhance bidirectional communication with you, our valued community, and make Node-RED's power more accessible than ever.</p>
<img src="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/images/IMG_6334.jpg" width="500" />
<h2 id="announcements-tldr" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#announcements-tldr"></a> Announcements TLDR</h2>
<p>We're excited to share some significant enhancements that will elevate your experience with FlowFuse and Node-RED. We've announced that Dashboards 2.0 are in Beta version. Recent additions to Dashboards 2.0 include integrating Vuetify and Mermaid into Dashboard 2.0 unlocking a suite of custom UI components to enrich your dashboards. Our step-by-step guide will walk you through creating dynamic elements like countdown timers, leveraging the power of VueJS for seamless updates.</p>
<p>We're also thrilled to introduce FlowFuse Blueprints - a game-changer in building bespoke, flexible, and resilient manufacturing applications. Our initial set of Blueprints, including ANDON Operator Terminal, Performance Overview Dashboard, and OEE Calculator, provide preconfigured Node-RED applications that streamline your development process. Dive into the world of Blueprints and discover how they can accelerate your project's journey from concept to operational reality. Join the <a href="https://flowfuse.com/webinars/2023/blueprints/">webinar</a> to find out more.</p>
<h2 id="recent-changelog-updates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#recent-changelog-updates"></a> Recent Changelog Updates</h2>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/">Integrate your own widgets with Dashboard 2.0</a></li>
<li><a href="https://flowfuse.com/changelog/2023/10/blueprints/">Blueprints</a></li>
<li><a href="https://flowfuse.com/changelog/2023/10/device-snapshot-selection/">Enhanced Snapshot Selection</a></li>
<li><a href="https://flowfuse.com/changelog/2023/10/path-bug-fix/">Device Agent path bug fix</a></li>
<li><a href="https://flowfuse.com/changelog/2023/10/resource-alerts/">Resource Monitoring in Audit Log</a></li>
<li><a href="https://flowfuse.com/changelog/2023/10/certified-nodes/">Certified Nodes</a></li>
</ul>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#upcoming-events"></a> Upcoming events</h2>
<h3 id="flowfuse-blueprints%3A-your-pathway-to-enhanced-manufacturing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#flowfuse-blueprints%3A-your-pathway-to-enhanced-manufacturing"></a> FlowFuse Blueprints: Your Pathway to Enhanced Manufacturing</h3>
<p>Explore building manufacturing applications with FlowFuse Blueprints in our upcoming webinar. Blueprints make it easy to get started building applications with Node-RED.</p>
<p><a href="https://flowfuse.com/webinars/2023/blueprints/">Sign-up today</a> to join us on November 30th.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#from-our-blog"></a> From our Blog</h2>
<ul>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/meet-us-at-sps-nuremberg/">Meet FlowFuse at SPS Nuremberg</a> - Talk about Node-RED and how FlowFuse can help you operationalize your flows!</p>
</li>
<li>
<p><a href="https://flowfuse.com/node-red/hardware/raspberry-pi-5/">Install the FlowFuse Edge Agent on the Raspberry Pi 5</a> - Managing your Raspberry Pi 5 with Node-RED through FlowFuse is easy to set up</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/">Innovate from within - Why manufacturing must embrace Citizen Developers</a> - Empower your Operational Technology teams as Citizen Developers</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/">Embracing Innovation: Build vs Buy in MES</a> - Bridging the Gap: Uniting MES Development with Automation System Practices</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/">What are FlowFuse Blueprints?</a> - Preconfigured Node-RED Applications</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/">Integrate your own widgets with Dashboard 2.0</a> - With the 0.6.0 Release of Dashboard 2.0, we now support third-party widget integration. Read more in this deep dive.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/">What are FlowFuse Blueprints?</a> - Preconfigured Node-RED Applications</p>
</li>
</ul>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/community-news-11/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/meet-us-at-sps-nuremberg/Meet FlowFuse at SPS NurembergTalk about Node-RED and how FlowFuse can help you operationalize your flows!2023-11-08T00:00:00ZZJ van de Weg<p>FlowFuse is excited to be exhibiting at the SPS in Nuremberg next week. We will
be located in Hall 5 Booth 145.</p>
<p>At the SPS, we will be showcasing our latest FlowFuse platform, which is a
powerful and user-friendly tool for creating and managing Node-RED flows. There's
also an option to get a demonstration of how FlowFuse can be used to solve a
variety of industrial automation problems.</p>
<p>We are also keen to meet with Node-RED users and FlowFuse prospective customers
at the SPS. If you would like to book a meeting with us, please visit our
<a href="https://flowfuse.com/contact-us/">contact us</a> page.</p>
<p>We look forward to seeing you at the SPS!</p>
<!--more-->
<h3 id="about-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/meet-us-at-sps-nuremberg/#about-flowfuse"></a> About FlowFuse</h3>
<p>FlowFuse is a software company that develops tools for creating and managing
Node-RED flows. Node-RED is a popular open-source platform for flow-based programming. FlowFuse makes it easy to create and manage complex Node-RED flows, even for users with no prior programming experience.</p>
<p>FlowFuse is used by a wide range of organizations, including manufacturers, utilities, and research institutions. FlowFuse is used to solve a variety of industrial automation problems, such as data acquisition, data enrichment,
and process monitoring.</p>
<h3 id="about-sps-nuremberg" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/11/meet-us-at-sps-nuremberg/#about-sps-nuremberg"></a> About SPS Nuremberg</h3>
<p>SPS Nuremberg is the world's leading trade fair for electric automation. The fair takes place annually in Nuremberg, Germany. SPS Nuremberg is a showcase for the latest innovations in industrial automation.</p>
<p>We hope to see you at the SPS!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/Innovate from within - Why manufacturing must embrace Citizen DevelopersEmpower your Operational Technology teams as Citizen Developers2023-10-30T00:00:00Z<p>In the early days when I was working as a solution architect, I found myself in a peculiar position, observing an intriguing relationship between Operational Technology (OT) and Information Technology (IT) departments. While OT departments struggled to develop digital solutions, the IT departments held an unspoken authority in this domain, leading to a paradoxical dynamic that often seemed to hinder rather than facilitate progress.</p>
<!--more-->
<p>As a solution architect working within this environment, I found myself uniquely positioned to bridge the gap between these two groups while also helping them find common ground on which they could build successful projects together. To do so effectively, I needed to understand each group's perspective and determine how best they could collaborate without compromising either side's autonomy or objectives. This task was not always easy but ultimately proved rewarding once achieved successfully.</p>
<h2 id="a-tale-of-shadow-it" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/#a-tale-of-shadow-it"></a> A Tale of Shadow IT</h2>
<p>In the best-case scenarios, IT departments considered themselves as vendors, and OT, the customers. However, the IT-driven, off-the-shelf solutions seldom proved to be the right fit for the problems at hand. This mismatch often led to the birth of shadow IT systems—solutions that were not sanctioned by IT, yet were deemed necessary to maintain operational efficiency.
In this realm of shadow IT, I've observed countless solutions running on desktop PCs, hidden from the eyes of IT departments, solving problems in manufacturing quickly—a situation born of necessity, with which no one could be satisfied. I cannot deny that Node-RED, an open-source low-code programming tool, has very often been one of these shadow IT systems, enabling data modification, lightweight HMI creation, and empowering the OT workforce.</p>
<p>However, this under-the-radar approach is fraught with dangers. With unprotected systems and without official oversight, the potential for security breaches is a real and present danger. I would wager that even today, a simple scan on port 1880 would reveal a multitude of open Node-RED environments. This double-edged sword of innovation and insecurity highlights the urgent need for a paradigm shift.</p>
<h2 id="why-are-citizen-developers-important-in-manufacturing%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/#why-are-citizen-developers-important-in-manufacturing%3F"></a> Why are Citizen Developers Important in Manufacturing?</h2>
<p>Citizen developers are individuals who create applications and software solutions for their organizations without any background in computer programming. These citizen developers leverage low-code platforms to build apps that automate tasks, streamline processes, and improve existing systems' user experiences. Citizen developers are a growing trend in the industry, serving as a key factor in harmonizing IT and OT interests. They bring an array of benefits that can make businesses more efficient and cost-effective. From reducing development costs to increasing productivity, citizen developers offer significant value to organizations seeking ways to stay competitive.</p>
<ol>
<li>
<p><strong>Acceleration of Software Development:</strong> Low-code platforms significantly expedite the software development process, enabling more efficient realization of business requirements. By quickening development, these platforms free up time and resources for design and innovation, leading to higher-quality software that better serves the business's needs.</p>
</li>
<li>
<p><strong>Crafting Bespoke Applications:</strong> By empowering non-professional coders with the right tools and resources, organizations can democratize digital solutions. This not only facilitates the creation of custom systems that fit seamlessly into existing infrastructures but also enables rapid ideation and development, even for those without prior coding experience.</p>
</li>
<li>
<p><strong>Enhancement of Operational Processes and Customer Experiences:</strong> With the automation capabilities of low-code platforms and the innovative solutions generated by citizen developers, operational processes can be optimized, and customer experiences significantly improved. The synergy between these elements can lead to efficiency gains through automation and the delivery of more effective, customer-centric solutions.</p>
</li>
</ol>
<p>This shift toward embracing citizen development within manufacturing represents a unique opportunity where both sides benefit significantly; not only do OT departments gain access to custom applications tailored precisely for their needs, but IT teams can lead guidance for self-empowerment, resulting in greater efficiency through automation.</p>
<h2 id="democratize-application-development-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/#democratize-application-development-with-node-red"></a> Democratize Application Development with Node-RED</h2>
<p>For the past decade, Node-RED has been a pioneer in low-code tools, democratizing application development in manufacturing. It provides powerful data collection, transformation, and visualization capabilities, simplifying the application-building process.
As an open-source tool with no associated costs or licensing fees, Node-RED is <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#the-standard-for-edge-computing-and-plcs">increasingly popular</a> among manufacturers seeking to quickly develop custom applications without extensive coding knowledge or experience.
FlowFuse takes things one step further, offering organizations a way to officially incorporate and scale Node-RED, making it compliant, governed, and secure within the existing solution landscape.</p>
<h2 id="the-strategic-imperative-of-citizen-development" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/citizen-development/#the-strategic-imperative-of-citizen-development"></a> The Strategic Imperative of Citizen Development</h2>
<p>For industry decision-makers, this is a call to action. It is imperative to nurture this growing community within your workforce, providing them with the tools, platforms, and, importantly, the organizational support they require. The empowerment of citizen developers could very well be the deciding factor in your organization's ability to stay competitive, agile, and innovative in a rapidly evolving market.</p>
<p>Looking to the future, the success of modern manufacturing lies in its people and their ability to solve problems with the right tools at their fingertips. Citizen development has the potential to break down the barriers. Organizations that recognize and invest in this potential will undoubtedly lead the charge.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/What are Certified Nodes?Enhanced Security, Quality, and Support2023-10-27T00:00:00Z<p>We are thrilled to introduce a new feature for our Teams and Enterprise Tier customers - <strong>Certified Nodes for Node-RED</strong>. This new offering is designed to reinforce your flows's robustness by granting you access to Node-RED nodes that stand up to our rigorous quality and security standards.</p>
<!--more-->
<h2 id="what-is-a-certified-node%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/#what-is-a-certified-node%3F"></a> What is a certified node?</h2>
<p>A certified node is a module from the Node-RED library that undergoes a certification process, ensuring it adheres to standards that address three core pillars:</p>
<p><strong>Quality</strong></p>
<ul>
<li>Testing phases for each node.</li>
<li>Operational reliability and compatibility.</li>
</ul>
<p><strong>Security</strong></p>
<ul>
<li>Proactive resolution of potential vulnerabilities.</li>
<li>Revocation of certification for nodes falling short on security, with prompt notifications to affected customers.</li>
</ul>
<p><strong>Support</strong></p>
<ul>
<li>Ambitious aim towards effective issue resolution.</li>
<li>Assistance for troubleshooting.</li>
</ul>
<h2 id="how-to-use-certfied-nodes%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/#how-to-use-certfied-nodes%3F"></a> How to use certfied nodes?</h2>
<p>Accessing certified nodes is straightforward—they're integrated directly within your Node-RED palette manager, simplifying selection and implementation. All new instances since October 26, 2023, have automatic access to the catalogue. If you want to add Certified Nodes to one of your existing instances, just <a href="https://flowfuse.com/support/">contact us</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/certified-nodes-OjDKSFWsS4-568.avif 568w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/certified-nodes-OjDKSFWsS4-568.webp 568w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Node-RED palette manager" loading="lazy" decoding="async" src="https://flowfuse.com/img/certified-nodes-OjDKSFWsS4-568.jpeg" width="568" height="412" /></picture></p>
<h2 id="what-kind-of-nodes-are-included%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/#what-kind-of-nodes-are-included%3F"></a> What kind of Nodes are included?</h2>
<p>Our initial roll-out includes a curated assortment of certified nodes. We're continuously expanding our library, with upcoming weeks bringing a wider array of options. Should you find a gap in your desired functionalities, we encourage you to <a href="https://discourse.nodered.org/c/vendors/flowfuse/24/">reach out</a>. Your feedback drives our journey forward, influencing the nodes we introduce next.</p>
<ul>
<li>Linting for Node-RED</li>
<li>Enhanced debugging for Node-RED</li>
<li>FlowFuse Snapshot Plugin</li>
<li>Node-RED Dashboard 2.0</li>
<li>E-Mail Communication</li>
<li>Base64 converter</li>
<li>Buffer Parser</li>
<li>PostgreSQL</li>
<li>InfluxDB</li>
<li>Omron PLC</li>
<li>MC Protocol (Mitsubishi PLCs)</li>
</ul>
<h3 id="what-will-follow-in-the-coming-weeks%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/certified-nodes/#what-will-follow-in-the-coming-weeks%3F"></a> What will follow in the coming weeks?</h3>
<ul>
<li>Siemens PLC</li>
<li>Modbus</li>
<li>Mssql</li>
<li>Rockwell and Allen Bradley</li>
<li>OPC-UA</li>
<li>MongoDB</li>
<li>MySQL</li>
<li>WhatsAPP, Telegram, Slack</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/Embracing Innovation: Build vs Buy in MESBridging the Gap: Uniting MES Development with Automation System Practices2023-10-20T00:00:00Z<p>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.</p>
<!--more-->
<h2 id="the-hybrid-approach%3A-a-best-of-both-worlds-strategy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#the-hybrid-approach%3A-a-best-of-both-worlds-strategy"></a> The Hybrid Approach: A Best of Both Worlds Strategy</h2>
<p>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.</p>
<h2 id="node-red%3A-the-mes-code's-best-friend" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#node-red%3A-the-mes-code's-best-friend"></a> Node-RED: The MES Code's Best Friend</h2>
<p>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.</p>
<h2 id="the-power-of-standardization-and-code-clarity" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#the-power-of-standardization-and-code-clarity"></a> The Power of Standardization and Code Clarity</h2>
<p>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.</p>
<h2 id="flowfuse%3A-bridging-the-gap-to-deployment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#flowfuse%3A-bridging-the-gap-to-deployment"></a> FlowFuse: Bridging the Gap to Deployment</h2>
<p>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.</p>
<h2 id="pioneering-the-future%3A-the-strategic-adoption-of-node-red-and-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#pioneering-the-future%3A-the-strategic-adoption-of-node-red-and-flowfuse"></a> Pioneering the Future: The Strategic Adoption of Node-RED and FlowFuse</h2>
<p>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.</p>
<h2 id="elevate-your-mes-strategy%3A-embrace-node-red-and-flowfuse-today" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/mes-build-buy/#elevate-your-mes-strategy%3A-embrace-node-red-and-flowfuse-today"></a> Elevate Your MES Strategy: Embrace Node-RED and FlowFuse Today</h2>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/service-disruption-report-2023-10-11/Service Disruption Report for October 11th, 20232023-10-18T00:00:00ZNick O'Leary<p>On October 11th, 2023, we had an issue where users were not able to access the
Node-RED editor, recieving a 'Access Denied' error message.
This post examines the issue that was hit, the timeline of events and
what we've done to resolve it.</p>
<!--more-->
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/service-disruption-report-2023-10-11/#summary"></a> Summary</h2>
<p>As part of our company rebranding, we planned a migration for our FlowFuse Cloud
platform from <code>app.flowforge.com</code> to <code>app.flowfuse.com</code>.</p>
<p>We applied this change in co-ordination with our customers using Single Sign On
as it required an update to their configuration to match.</p>
<p>This was done on Tuesday October 10th and all confirmed working with those customers.</p>
<p>On Wednesday October 11th we received two reports that separate users could not
access their Node-RED editors. We quickly identified the issue was related to
how the Node-RED editors authenticated users against the platform for non-SSO users.</p>
<p>A workaround was identified to ensure users were logged in via the new domain.</p>
<p>We then looked at options to mitigate this for other users. We could not roll
back the domain name migration as it would have required co-ordinated action with
multiple SSO customers - who were not otherwise impacted by this issue.</p>
<h2 id="resolution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/service-disruption-report-2023-10-11/#resolution"></a> Resolution</h2>
<p>We tested various approaches of adding automatic redirection from one domain to
the other. Due to the fact all existing Instances and Devices had the old domain
name hardcoded into their settings, we were limited in what we could do here.</p>
<p>We ultimately applied a single redirect for <code>https://app.flowforge.com</code> to
<code>https://app.flowfuse.com</code> - without any redirecting of paths beneath either domain.</p>
<p>This URL is only accessed by real users when coming to log into the platform. By
redirecting at that point in time, it ensures they are logged into the new domain
and everything works as expected.</p>
<p>For users with active sessions on the old domain, a simple log out and log back in
will get them on to the new domain.</p>
<h2 id="next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/service-disruption-report-2023-10-11/#next-steps"></a> Next Steps</h2>
<p>Having resolved the immediate issue we looked at how this situation came to happen
and why it wasn't caught in our preparation for the migration.</p>
<p>We have a staging environment where we verify any changes before they get applied
to production. We all have SSO enabled in that environment and a small number of
test users without SSO enabled.</p>
<p>Our testing had focused on the SSO users which, by virtue of the SSO process, ensured
they ended up logged into the new domain.</p>
<p>The testing done with non-SSO users was more limited and didn't hit the right combination
of having existing log sessions on one or other of the domains to match the scenario
hit by our customers.</p>
<p>We also identified some items to follow up on around how the existing Instances
and Devices handle HTTP redirects. Currently, the Device Agent is not configured
to follow redirects. That is a change we have <a href="https://github.com/FlowFuse/device-agent/issues/182">added to the backlog</a>.</p>
<h2 id="timeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/service-disruption-report-2023-10-11/#timeline"></a> Timeline</h2>
<p><em>All times are BST.</em></p>
<p><strong>Tuesday October 10th</strong></p>
<p>We updated the platform's primary domain name to <code>app.flowfuse.com</code> whilst keeping <code>app.flowforge.com</code> active. This was done in co-ordination with our SSO customers who needed to make an update to their SSO configuration at the same time. Both customers reported success following the change.</p>
<p>Our own validation demonstrated we could login via our own SSO, access editors, and devices continued to work as before (in particular, device editor and snapshot gathering).</p>
<p><strong>Wednesday October 11th</strong></p>
<ul>
<li><strong>11:50</strong> and <strong>11:57</strong> - we received two support request from a user getting an 'access denied' error when trying to access an editor.</li>
<li><strong>12:09</strong> - Workaround shared with both customers to login via the new domain first</li>
<li><strong>12:30</strong> - We applied a blanket redirect for <code>app.flowforge.com</code> to <code>app.flowfuse.com</code>.</li>
<li><strong>12:50</strong> - We then reduced the scope of the redirect so that devices would not be impacted</li>
<li><strong>13:14</strong> - A secondary issue with logging into the editor when logged in on the new domain was reported internally.</li>
<li><strong>13:44</strong> - Reverted all of the redirect handling whilst reviewing the problems with the previous redirects.</li>
<li><strong>14:20</strong> - We applied a redirect to just the root of the domain and documented for our support channel</li>
</ul>
<p>No further reports were received after this time.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/What are FlowFuse Blueprints?Preconfigured Node-RED Applications2023-10-16T00:00:00Z<p>Starting today, FlowFuse Blueprints are available on FlowFuse Cloud. Additionally, upon request, all our Teams and Enterprise Self-Hosted customers gain access to this collection. But what exactly are FlowFuse Blueprints?</p>
<!--more-->
<h2 id="flowfuse-blueprints" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#flowfuse-blueprints"></a> FlowFuse Blueprints</h2>
<p>FlowFuse Blueprints aim to make the Node-RED experience more accessible for newcomers, while also offering a treasure trove of fresh ideas for seasoned Node-RED users. When setting up a new Node-RED instance, you now have the option to choose a blueprint tailored for specific use cases. For example, our "ANDON Operator Terminal" blueprint can be selected, and it will automatically configure the Node-RED instance, sparing you the need to start from scratch. While these templates are powerful out-of-the-box, they're also fully customizable, allowing you to tweak them to suit your unique requirements. Ultimately, blueprints speed up the learning curve for new users and expedite the solution-building process for experienced ones.</p>
<h3 id="how-to-use-blueprints%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#how-to-use-blueprints%3F"></a> How to use Blueprints?</h3>
<p>All our FlowFuse Cloud users can select a Blueprint directly while creating a new Node-RED instance. Self-hosted customers can request access to our blueprints via a <a href="https://flowfuse.com/support/">support ticket</a>.</p>
<h2 id="the-first-three-blueprints" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#the-first-three-blueprints"></a> The first three Blueprints</h2>
<p>In the coming weeks, we'll be releasing a multitude of blueprints tailored for diverse use cases. However, we decided to start with with three foundational manufacturing applications designed with the <a href="https://dashboard.flowfuse.com/">Node-RED Dashboard 2.0</a>.</p>
<h3 id="andon-operator-terminal" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#andon-operator-terminal"></a> ANDON Operator Terminal</h3>
<p>The Andon Operator Terminal is designed to be at the start of an Andon process, allowing end-users to report any issues with the cell to a supervisor.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/ANDON1-mOan0CEMsR-650.avif 650w, https://flowfuse.com/img/ANDON1-mOan0CEMsR-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ANDON1-mOan0CEMsR-650.webp 650w, https://flowfuse.com/img/ANDON1-mOan0CEMsR-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ANDON1-mOan0CEMsR-650.jpeg 650w, https://flowfuse.com/img/ANDON1-mOan0CEMsR-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ANDON Blueprint Screenshot" loading="lazy" decoding="async" src="https://flowfuse.com/img/ANDON1-mOan0CEMsR-650.jpeg" width="1300" height="741" /></picture></p>
<h3 id="performance-overview-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#performance-overview-dashboard"></a> Performance Overview Dashboard</h3>
<p>The Performance Overview Dashboard Blueprint provides a real-time snapshot of key performance metrics, delivering a comprehensive overview of manufacturing operations for a specific station or entire line.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-650.avif 650w, https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-650.webp 650w, https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-650.jpeg 650w, https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Performance Overview Screenshot" loading="lazy" decoding="async" src="https://flowfuse.com/img/performance-dashboard-5-FqFZ_6h_-650.jpeg" width="1300" height="585" /></picture></p>
<h3 id="oee-calculator" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/blueprints/#oee-calculator"></a> OEE Calculator</h3>
<p>If automatic calculations are not feasible, the OEE Calculator Blueprint enables end-users to manually input production data to compute the Overall Equipment Effectiveness (OEE) for a given machine.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-650.avif 650w, https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-650.webp 650w, https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-650.jpeg 650w, https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="OEE Calculator Screenshot" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-data-YSqQcKPnn8-650.jpeg" width="1300" height="605" /></picture></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/Integrate your own widgets with Dashboard 2.0With the 0.6.0 Release of Dashboard 2.0, we now support third-party widget integration. Read more in this deep dive.2023-10-06T00:00:00ZJoe Pavitt<p>With a new release, comes new features for Dashboard 2.0, and the focus of this release has been on improving the developer experience for those building third-party widgets for Dashboard 2.0.</p>
<!--more-->
<p>Dashboard 1.0 had a hugely popular ecosystem of third party widgets (e.g. <code>ui-worldmap</code>, <code>ui-svg</code>) and something we've been keen to support is a platform where these widgets (and more) can be built and used within Dashboard 2.0 too.</p>
<p>Whilst we can't support the existing Dashboard 1.0 extensions directly (given that we're now VueJS-based, rather than AngularJS), we hope that the framework, documentation and this article, will help springboard the community to build new (and transfer over old) widgets for Dashboard 2.0.</p>
<h2 id="building-from-ui-template" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/#building-from-ui-template"></a> Building from <code>ui-template</code></h2>
<p>As with Dashboard 1.0, we've utilised the flexibility of our <code>ui-template</code> node here to enable third-party integrations.</p>
<p>If you're used the new <code>ui-template</code> in Dashboard 2.0 already, you'll know that you can provide raw Vue (HTML) content and it'll render that into your Dashboard. In 0.6.0, we've added <em>a lot</em> of new functionality to the guts of <code>ui-template</code>, which we can then extend with our third-party widgets.</p>
<p>This new functionality includes:</p>
<ul>
<li><strong>Custom Dependencies</strong> - Injection of external widget dependencies (e.g. other JavaScript libraries) via <code><head></code>.</li>
<li><strong>On Input</strong> - <code>onInput</code> defines behaviour of the widget in Dashboard when it receives a message in Node-RED.</li>
<li><strong>On Load</strong> - <code>onMounted</code> defines functionality when a widget first loads in Dashboard.</li>
<li><strong>Custom Functions</strong> - Define general functions that can be called from within your widget at any point of your choosing</li>
<li><strong>Extend Built-In Events</strong> - Our built in <code>send</code> function can be called within your widget's template, and will send a message back to Node-RED, with any content of your choosing.</li>
<li><strong>Custom SocketIO Event Handlers</strong> - If you want to extend the communication between Dashboard and Node-RED, you can emit your own SocketIO events from Dashboard, and have respective handlers for those events in Node-RED.</li>
</ul>
<p>We also have plans to expose more of this new functionality to the <code>ui-template</code> interface itself within Node-RED, but for now it's mostly available when developing third-party widgets.</p>
<h2 id="useful-resources" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/#useful-resources"></a> Useful Resources</h2>
<p>If you're interested in building integrations, then we've also built a couple of resources to help you get started:</p>
<ul>
<li><a href="https://dashboard.flowfuse.com/contributing/widgets/third-party.html">Widget Development Guide</a> - A guide for how to structure your own widgets, and</li>
<li><a href="https://github.com/FlowFuse/node-red-dashboard-example-node">Example Integration (Repo)</a> - We've open sourced a very simple <code>ui-example</code> node that demonstrates how you can build your own widget for Dashboard 2.0, that utilises all of the features highlighted above.</li>
</ul>
<h2 id="what-else-is-new-in-0.6.0%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/dashboard-integrations/#what-else-is-new-in-0.6.0%3F"></a> What else is new in 0.6.0?</h2>
<p>Whilst we focussed this article on the third-party integrations, we did also squeeze quite a lot more into the 0.6.0 release too with plenty <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.6.0">other fixes and improvements</a>, including the separation of the Dash oard 2.0 nodes into a new "Dashboard 2" category in the Node-RED palette.</p>
<p>As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/Community News October 2023Your monthly update for the FlowFuse and Node-RED communities2023-10-05T00:00:00Z<p>Welcome to the FlowFuse newsletter for October 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="quicker-releases-for-flowfuse-cloud-and-new-changelog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#quicker-releases-for-flowfuse-cloud-and-new-changelog"></a> Quicker Releases for FlowFuse Cloud and New Changelog</h2>
<p>FlowFuse is committed to delivery new features to our customers and community as fast as possible. We are now rolling out new builds of FlowFuse to FlowFuse Cloud more regularly than the previous monthly release. This will allow FlowFuse Cloud users to benefit from the latest enhancements as they become available. The self-hosted version of FlowFuse will continue to have releases every 4 weeks.</p>
<p>To allow FlowFuse Cloud users to keep up to date on the new changes, we have started a <a href="https://flowfuse.com/changelog/">Changelog</a> to announce newly deployed features.</p>
<h2 id="recent-changelog-updates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#recent-changelog-updates"></a> Recent Changelog Updates</h2>
<ul>
<li><a href="https://flowfuse.com/changelog/2023/09/devops-actions/">DevOps Pipeline with action selection</a></li>
<li><a href="https://flowfuse.com/changelog/2023/09/snapshots-devices/">Usability improvements to Device Management</a></li>
<li><a href="https://flowfuse.com/changelog/2023/09/introduction-enterprise-tier/">Introducing the Enterprise tier</a></li>
<li><a href="https://flowfuse.com/changelog/2023/09/pipeline-api/">API Endpoints for DevOps Pipeline</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/">Private Node Support</a></li>
</ul>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#upcoming-events"></a> Upcoming events</h2>
<h3 id="dashboard-2.0---where-we-are%2C-and-what%E2%80%99s-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#dashboard-2.0---where-we-are%2C-and-what%E2%80%99s-next%3F"></a> Dashboard 2.0 - Where we are, and what’s next?</h3>
<p>The next generation of the Node-RED dashboard is starting to mature. Development work on Dashboard 2.0 started earlier this year and amazing progress has been made already. Join Joe Pavitt, leader of the Dashboard 2.0 project, who will demonstrate the new dashboard project and discuss future plans during our October webinar.</p>
<p><a href="https://flowfuse.com/webinars/2023/dashboard-20/">Sign-up today</a> to join us on October 26.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#from-our-blog"></a> From our Blog</h2>
<ul>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/">Custom Vuetify components for Dashboard 2.0</a> - the new dashboard project allows for custom UI components</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/">Updating our branding across GitHub, npm and Dockerhub</a> - our GitHub, npm and DockerHub locations are now called FlowFuse, due to the rename from FlowForge.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/">How ChatGPT improves Node-RED Developer Experience</a> - lots of interest in ChapGPT in the Node-RED community. A quick review of different integrations.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/">Share & Preview Flows on flows.nodered.org</a> - A new way to visualize flows in web pages.</p>
</li>
<li>
<p><a href="https://flowfuse.com/node-red/integration-technologies/rest/">Charting REST API Data in a Dashboard</a> - A short tutorial on how to gather data from a REST API for a dashboard.</p>
</li>
<li>
<p>Modernize your legacy industrial data - Two part series on making sense of your industrial data.</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/">Part 1</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/">Part 2</a></li>
</ul>
</li>
</ul>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/community-news-10/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/How to Use Private Custom Nodes in FlowFuse?Run your own private Node-RED catalogue and npm repository for custom nodes2023-10-02T00:00:00ZBen Hardill<p>With version 1.12 of FlowFuse, it is now possible to use your custom nodes. In this article, we'll explain how to do that.</p>
<!--more-->
<div class="blog-update-notes">
<p><strong>UPDATE:</strong> Since this article was published, we've made this even easier on FlowFuse!</p>
<p>Now, FlowFuse includes a private registry for all Team and Enterprise Tier customers, so there is no need to host and manage your own.</p>
<p>You can view our documentation on this feature <a href="https://flowfuse.com/docs/user/custom-npm-packages/">here</a></p>
</div>
<p>What do we mean by custom nodes? Typically, Node-RED nodes are hosted publicly on the npmjs registry, making them accessible to everyone for download and contribution. However, there are use cases where you may not want to share your developed nodes publicly. In such scenarios, it becomes necessary to run your own private Node-RED catalog and npm repository. This approach allows you to manage your custom nodes securely and efficiently.</p>
<h2 id="step-1---setting-up-a-private-npm-repository" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#step-1---setting-up-a-private-npm-repository"></a> Step 1 - Setting Up a Private npm Repository</h2>
<p>Before you can use custom nodes, you'll need a place to store them.</p>
<h3 id="option-1---service-provider" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#option-1---service-provider"></a> Option 1 - Service Provider</h3>
<p>Choose a public service provider, like <a href="https://www.npmjs.com/">npmjs</a>, that allows you to host private packages and upload your node module.</p>
<h3 id="option-2---verdaccio" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#option-2---verdaccio"></a> Option 2 - Verdaccio</h3>
<p>Another option is to use Verdaccio, a lightweight private npm proxy registry that allows you to run your own registry.</p>
<h4 id="installing-verdaccio" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#installing-verdaccio"></a> Installing Verdaccio</h4>
<ol>
<li>Install Verdaccio using npm:</li>
</ol>
<div style="position: relative" id="code-container-36">
<pre class="language-sh"><code id="code-36" class="language-sh"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> verdaccio</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-36" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Run Verdaccio:</li>
</ol>
<div style="position: relative" id="code-container-44">
<pre class="language-sh"><code id="code-44" class="language-sh">verdaccio</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-44" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will start Verdaccio on <code>http://localhost:4873</code></p>
<h4 id="configuring-verdaccio" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#configuring-verdaccio"></a> Configuring Verdaccio</h4>
<p>The default configuration supports scoped packages and allows any user to access all packages, although only authenticated users can publish.</p>
<p>If necesarry you can edit the Verdaccio configuration file, usually found at <strong>~/.config/verdaccio/config.yaml</strong>.</p>
<p>Refer to the <a href="https://verdaccio.org/docs/configuration/">documentation</a> for all configuration options.</p>
<p>It is important that if you intend to use a private NPM registry with FlowFuse Cloud, the registry will need to be publicly exposed to the internet. Please make sure you understand how to secure it appropriately.</p>
<h4 id="publish-your-package" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#publish-your-package"></a> Publish your package</h4>
<ol>
<li>Create a user</li>
</ol>
<div style="position: relative" id="code-container-73">
<pre class="language-sh"><code id="code-73" class="language-sh"><span class="token function">npm</span> adduser <span class="token parameter variable">--registry</span> http://localhost:4873/</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-73" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ol start="2">
<li>Publish you package</li>
</ol>
<div style="position: relative" id="code-container-81">
<pre class="language-sh"><code id="code-81" class="language-sh"><span class="token function">npm</span> publish <span class="token parameter variable">--registry</span> http://localhost:4873/</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-81" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h2 id="step-2---creating-your-private-node-red-catalog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#step-2---creating-your-private-node-red-catalog"></a> Step 2 - Creating Your Private Node-RED Catalog</h2>
<p>There are several ways to generate your own <code>catalogue.json</code>, which is necessary for Node-RED to understand which packages are available where. Below, we'll show you two of the many options to create and host a <code>catalogue.json</code>.</p>
<h3 id="option-1---web-app" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#option-1---web-app"></a> Option 1 - Web App</h3>
<p>To create and host a Node-RED catalog, we recommend the package <a href="https://github.com/hardillb/node-red-private-catalogue-builder"><code>node-red-private-catalogue-builder</code></a>.</p>
<p>The container accepts the following environment variables:</p>
<ul>
<li>PORT - Which port to listen on (defaults to 3000)</li>
<li>HOST - Which local IP Address to bind to (defaults to 0.0.0.0)</li>
<li>REGISTRY - A host and optional port number to connect to the NPM registry (defaults to http:/ registry:4873)</li>
<li>KEYWORD - The npm keyword to filter on (defaults to Node-RED)</li>
</ul>
<p><strong>It presents 2 HTTP endpoints</strong></p>
<ul>
<li>/update - a POST to this endpoint will trigger a rebuild of the catalogue</li>
<li>/catalogue.json - a GET request returns the current catalogue</li>
</ul>
<p>The <code>/update</code> endpoint can be used with the Verdaccio <a href="https://verdaccio.org/docs/notifications">notification</a> events to trigger the catalogue to automatically when nodes are added or updated.</p>
<div style="position: relative" id="code-container-137">
<pre class="language-yaml"><code id="code-137" class="language-yaml"><span class="token key atrule">notify</span><span class="token punctuation">:</span><br /> <span class="token key atrule">method</span><span class="token punctuation">:</span> POST<br /> <span class="token key atrule">headers</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token key atrule">'Content-Type'</span><span class="token punctuation">:</span> <span class="token string">'application/json'</span><span class="token punctuation">}</span><span class="token punctuation">]</span><br /> <span class="token key atrule">endpoint</span><span class="token punctuation">:</span> http<span class="token punctuation">:</span>//localhost<span class="token punctuation">:</span>3000/update<br /> <span class="token key atrule">content</span><span class="token punctuation">:</span> <span class="token string">'{"name": "", "versions": "", "dist-tags": ""}'</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-137" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="option-2---node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#option-2---node-red"></a> Option 2 - Node-RED</h3>
<p>You can also use a FlowFuse Node-RED instance and the <a href="https://flows.nodered.org/node/node-red-contrib-catalogue"><code>node-red-contrib-catalogue</code></a> package to generate and host your <code>catalogue.json</code> file.</p>
<iframe width="100%" height="100%" src="https://flows.nodered.org/flow/1f01a92fdbd4172c75fcb88b44e64954/share" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<h2 id="step-3---flowfuse-configuration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/use-private-custom-nodes-with-flowfuse/#step-3---flowfuse-configuration"></a> Step 3 - FlowFuse configuration</h2>
<p>Next, you'll need to add all the details to your FlowFuse instance configuration.</p>
<ol>
<li>Add the Catalog: Go to your <strong>Instance</strong> -> <strong>Settings</strong> -> <strong>Palette</strong>. Here, you'll have the option to add a catalogue.json. You'll need to provide the URL from which the catalogue.json can be accessed.</li>
</ol>
<p>For example: <strong>https://catalogue.nodered.org/catalogue.json</strong></p>
<p>It is import to remember that this URL must be accessible from the browser running the Node-RED editor and when used with FlowFuse (or any other Node-RED editor accessed via HTTPS) it must be served with HTTPS.</p>
<ol start="2">
<li>Modify the npmrc File: You'll need to configure where to find the packages from the catalog, possibly specifying a scope.</li>
</ol>
<pre><code># Set a new registry for a scoped package
@myscope:registry=https://mycustomregistry.example.org
</code></pre>
<p>If necessary, set authentication-related configurations. See the <a href="https://docs.npmjs.com/cli/v9/configuring-npm/npmrc#auth-related-configuration">documentaion</a> for details.</p>
<ol start="3">
<li>Save and Restart Your Node-RED Instance: The new npm modules should now be visible in the Node-RED Palette Manager.</li>
</ol>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/Custom Vuetify components for Dashboard 2.0Expand your dashboard with the full collection of Vuetify components2023-10-02T00:00:00ZZJ van de Weg<p>Vuetify is a library of UI components using Vue. This saves the developers of
Dashboard 2.0 a lot of time, but it can also help you, the end-user. As Vuetify
is now included, it can be used to include <em>any</em> of their components. So in this
post we're going to use a few of these to teach you how to use any of them.</p>
<!--more-->
<p>Let's install the <a href="https://dashboard.flowfuse.com/getting-started.html">Dashboard 2.0 package</a> if you want to follow along. When that's done, let's figure
out how to build custom components on dashboards.</p>
<h2 id="custom-components" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/#custom-components"></a> Custom components</h2>
<p>While going through the list of components on <a href="https://vuetifyjs.com/en/components/">Vuetify</a>
there's several examples that aren't natively implemented in Dashboard 2.0.
One example we'll use in a dashboard in this post is the
<a href="https://vuetifyjs.com/en/components/progress-circular/">Progress circular</a> to
build a count down timer.</p>
<p>The documentation explains which elements one can change, in this case the size and
width. Having set those to the values you'd want in your dashboard, the HTML is
generated for you, in my case it's:</p>
<div style="position: relative" id="code-container-16">
<pre class="language-html"><code id="code-16" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-progress-circular</span> <span class="token attr-name">model-value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>20<span class="token punctuation">"</span></span> <span class="token attr-name">:size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>128<span class="token punctuation">"</span></span> <span class="token attr-name">:width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>12<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-progress-circular</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-16" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="using-the-template-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/#using-the-template-node"></a> Using the template node</h3>
<p>Like the <a href="https://flowfuse.com/node-red/core-nodes/template/">template core node</a>, the dashboard package
comes with <a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html">a template node of its own</a>.
If we take the HTML from the Vuetify docs pages and copy it in a template node
the spinner will show up on the dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/custom-element-dashboard-UyxiK6jozz-378.avif 378w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/custom-element-dashboard-UyxiK6jozz-378.webp 378w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Custom widget on Dashboard 2.0" alt=""Custom widget on Dashboard 2.0"" loading="lazy" decoding="async" src="https://flowfuse.com/img/custom-element-dashboard-UyxiK6jozz-378.jpeg" width="378" height="464" /></picture></p>
<h2 id="dynamic-templates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/#dynamic-templates"></a> Dynamic templates</h2>
<p>While a custom element on a page is cool, and shows you can inject arbitrary HTML
on a Dashboard, it's even better if we could make the element dynamic. So let's
start with a first dynamic element. The quickest way to get that done is have
an <a href="https://flowfuse.com/node-red/core-nodes/inject/"><code>Inject</code></a> node output a random number every second.</p>
<p>So let's hook up an Inject, with <code>msg.payload</code>'s output being a JSONata expression
<code>$round($random() * 100)</code> to generate a random number. And let's make sure it
sends a message every second.</p>
<p>Then we need to update the template node to the following snippet:</p>
<div style="position: relative" id="code-container-38">
<pre class="language-html"><code id="code-38" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>v-progress-circular</span> <span class="token attr-name">v-model</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>msg.payload<span class="token punctuation">"</span></span> <span class="token attr-name">:size</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>128<span class="token punctuation">"</span></span> <span class="token attr-name">:width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>12<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>v-progress-circular</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-38" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The difference is subtle, but important. Instead of hard-coding the <code>model-value</code>
to 20, the tag has changed name and it's set to <code>msg.payload</code>. The latter makes
the value dynamic.</p>
<p>Changing <code>model-value</code> to <code>v-model</code> is due to leaking implementation details of
Dashboard 2.0. It uses VueJS to provide, among other features, easy updating of
components. If components are dynamic, <em>always use <code>v-model</code></em>. This allows VueJS
to pick up changes made dynamically.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/random-progress-element-71UFWKWzzV-462.gif 462w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Progress spinner, random values" alt=""Progress spinner, random values"" loading="lazy" decoding="async" src="https://flowfuse.com/img/random-progress-element-71UFWKWzzV-462.webp" width="462" height="664" /></picture></p>
<h3 id="finishing-the-count-down-timer" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/10/custom-vuetify-components-dashboard/#finishing-the-count-down-timer"></a> Finishing the count down timer</h3>
<p>This is mostly a programmers job, but it's not hard, so let's get to it. A button
would be great to reset the timer, and for the sake of this post we can hardcode
the deadline to 1m from the button press.</p>
<p>When dragging in a button node, connect it to a <a href="https://flowfuse.com/node-red/core-nodes/change/">change</a>
node. In the change node set the flow variable <code>flow.deadline</code> to the timestamp. The
Inject node from earlier needs updating to inject the <code>flow.deadline</code>. All that's
left is calculating how many seconds passed, and normalizing 60 seconds to the
range between 0-100.</p>
<p>The complete flow is:</p>
<div id="nr-flow-129" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow129 = "\n[{\"id\":\"ce9bb8f74e3fc934\",\"type\":\"ui-template\",\"z\":\"24065a0aadb305e3\",\"group\":\"8fa772a709ae3316\",\"dashboard\":\"e5a3f4cdb11e5e3b\",\"page\":\"5bedf7f49d5a6037\",\"name\":\"Progress spinner\",\"order\":0,\"width\":0,\"height\":0,\"format\":\"<v-progress-circular v-model=\\\"msg.payload\\\" :size=\\\"128\\\" :width=\\\"12\\\"></v-progress-circular>\\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\":[{\"p\":\"payload\"}],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"($millis() - msg.payload)/1000\",\"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\":\"/dashboard\"},{\"id\":\"5bedf7f49d5a6037\",\"type\":\"ui-page\",\"name\":\"Page Name\",\"ui\":\"e5a3f4cdb11e5e3b\",\"path\":\"/\",\"layout\":\"grid\",\"theme\":\"8240fbe7c09bc81c\"},{\"id\":\"8240fbe7c09bc81c\",\"type\":\"ui-theme\",\"name\":\"Theme Name\",\"colors\":{\"surface\":\"#ffffff\",\"primary\":\"#0094ce\",\"bgPage\":\"#eeeeee\",\"groupBg\":\"#ffffff\",\"groupOutline\":\"#cccccc\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow129.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-129') })</script>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/Updating our branding across GitHub, npm and DockerhubRenaming our packages and containers and what it means for our users2023-09-27T00:00:00ZNick O'Leary<p>Following our rename to FlowFuse last month, we are about to take the next
set of steps to complete the rebrand. This time, focussed on the technical
assets we produce.</p>
<!--more-->
<p>Rebranding a company isn't a small undertaking, especially when your company
name is also your product name. When we announced our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/">new name last month</a> we
prioritised updating the website, our documentation and social media presences.
All of the most visible things relating to the company name.</p>
<p>But we knew that wasn't the whole job done. The name <code>flowforge</code> still appears
in the technical resources we use and the artefacts we publish. Changing them
is not as simple a task as changing some words on a website, so it has taken a bit
more time to get our plans in place for this next step.</p>
<p>I wanted to highlight the set of changes we'll be making in the coming days to
complete this migration. For the vast majority of users, expecially those using
FlowFuse Cloud, these changes will be completely transparent.</p>
<p>However, if you are contributing to any of our open source components, or consuming
our npm or Docker packages directly, then please read on.</p>
<p>There are four areas we need to migrate.</p>
<h3 id="github-organization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/#github-organization"></a> GitHub Organization</h3>
<p>As a company everything we do revolves around our GitHub Organization. Our
source code, release planning, this website, and far more all live there.</p>
<p>Step one of our migration will be renaming the organization to <code>FlowFuse</code>, so instead
of <code>https://github.com/flowforge</code> we will now live at <code>https://github.com/FlowFuse</code>.</p>
<p>Renaming organizations on GitHub, whilst not something done lightly, is well catered
for. Many existing urls should get automatically redirected - so any existing
links will still work. We will, of course, do the work to update any urls in our docs.</p>
<h3 id="npm-package-names" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/#npm-package-names"></a> NPM package names</h3>
<p>We publish a number of packages to the public Node.js Package Manager (npm) repository
under the <code>@flowforge</code> name.</p>
<p>After this week's release is done, we'll be updating all of our packages to publish
under the <code>@flowfuse</code> name and no longer updating the packages under the old name.</p>
<p>This will impact anyone who has installed any of our components directly from <code>npm</code>. For
example, the Device Agent or Node-RED Dashboard 2.0.</p>
<p>We will provide specific upgrade instructions for each of the affected components once
the move is done.</p>
<h3 id="docker-images" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/#docker-images"></a> Docker Images</h3>
<p>We publish container images to Dockerhub under the <code>flowforge</code> name. Once
we've updated our npm package names, we'll also be updating our container tags
to use the new name.</p>
<p>If you are using our helm or Docker Compose projects, we'll have a new release that
will help get you moved over to the new image names. Likewise our Digital Ocean
and AWS Marketplace offerings will be updated - and instructions provided for existing
users to migrate over.</p>
<h3 id="flowfuse-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/rebranding-our-components/#flowfuse-cloud"></a> FlowFuse Cloud</h3>
<p>The final step we have to make is to move FlowFuse Cloud over to its new home
at <code>app.flowfuse.com</code>. We have to co-ordinate the update with all of our customers
who use SSO to login to ensure they can continue to access the platform.</p>
<p>Once that is done, it will be a seamless transition for everyone. Existing Node-RED
instances will continue to use the <code>*.flowforge.cloud</code> domain, but then all new
instances will use the <code>*.flowfuse.cloud</code> domain.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/How ChatGPT improves Node-RED Developer ExperienceLanguage models have made an impact in the Node-RED community2023-09-23T00:00:00ZZJ van de Weg<p>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.</p>
<!--more-->
<h2 id="how-generative-ai-like-chatgpt-is-used-for-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/#how-generative-ai-like-chatgpt-is-used-for-node-red"></a> How generative AI like ChatGPT is used for Node-RED</h2>
<h3 id="function-node-%3C3-chatgpt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/#function-node-%3C3-chatgpt"></a> Function node <3 ChatGPT</h3>
<p>ChatGPT, and other models, can write code for you, much like <a href="https://github.com/features/copilot">GitHub CoPilot</a> or <a href="https://about.gitlab.com/gitlab-duo/">GitLab Duo</a>. 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’!</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-650.avif 650w, https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-650.webp 650w, https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-650.jpeg 650w, https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Example of Chat GPT to generate contents of a function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/chatgpt-fcn-example-izByhNHfVO-650.jpeg" width="1300" height="1159" /></picture></p>
<p>At FlowFuse we’ve written about this <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/">before</a>, and published a <a href="https://github.com/FlowFuse/node-red-function-gpt">plugin</a>. 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!</p>
<h3 id="flow-interpretation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/#flow-interpretation"></a> Flow Interpretation</h3>
<p>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.</p>
<p><img alt="ChatGPT Flow Interpretation" loading="lazy" decoding="async" src="https://raw.githubusercontent.com/node-red-jp/node-red-contrib-plugin-chatgpt/main/infotab.png" width="undefined" height="undefined" /></p>
<p><a href="https://www.linkedin.com/in/kazuhitoyokoi/">Kazuhito-san</a> 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
model. This is especially interesting as it's thus able regenerate it when changes were made by the developers.</p>
<p>It’s a plugin that requires very little setup, and can be found in the <a href="https://www.npmjs.com/package/node-red-contrib-plugin-chatgpt">flow library</a>.</p>
<h3 id="lots-of-plugins" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/#lots-of-plugins"></a> Lots of plugins</h3>
<p>The ecosystem of Node-RED has always been a fast adopter of new technology. There's
nodes for <a href="https://flows.nodered.org/node/node-red-contrib-chatgpt">ChatGPT</a>,
<a href="https://flows.nodered.org/node/node-red-contrib-bard">Google's Bard</a>, and many
more. These plugins genernally let you build on top of these models, and don't
nessecairly improve the developer experience. It's however a great source of
inspiration!</p>
<h3 id="further-discussion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/chatgpt-for-node-red-developers/#further-discussion"></a> Further discussion</h3>
<p>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?</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/Share & Preview Flows on flows.nodered.orgFlowFuse has just contributed an interactive "Flow Viewer" to flows.nodered.org, allowing users to preview flows, and embed them in articles & forum posts.2023-09-20T00:00:00ZJoe Pavitt<p>For years, Node-RED's website has provided functionality to share flows through <a href="https://flows.nodered.org/">flows.nodered.org</a></p>
<p>This week, we at FlowFuse have contributed a new feature to the site that allows users to visually preview those flows, and embed/share those flows in articles and on forum posts.</p>
<!--more-->
<h2 id="visual-flow-previews" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/#visual-flow-previews"></a> Visual Flow Previews</h2>
<p>A huge thank you for this work needs to go Gerrit Riessen's work published on his <a href="https://blog.openmindmap.org/">Open Mind Map Blog</a>. He recently open-sourced some great work to GitHub (<a href="https://github.com/gorenje/node-red-flowviewer-js">repo</a>), 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.</p>
<p>Adding this to <a href="https://flows.nodered.org/">flows.nodered.org</a> 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.</p>
<h3 id="example%3A-simple-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/#example%3A-simple-flow"></a> Example: Simple Flow</h3>
<p>Here's a demonstration of a simple <code>Inject</code> > <code>Debug</code> node:</p>
<iframe width="100%" height="200px" src="https://flows.nodered.org/flow/500ee13719e54e42493c8ec96fa733b6/share?height=100" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<h3 id="example%3A-subflows%2C-groups%2C-links-%26-switches" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/#example%3A-subflows%2C-groups%2C-links-%26-switches"></a> Example: Subflows, Groups, Links & Switches</h3>
<p>Here's a non-functional flow that just demonstrates how FlowViewer renders the range of node types available in Node-RED:</p>
<iframe width="100%" height="500px" src="https://flows.nodered.org/flow/82a8602b615740491d30c083e5292e5f/share" allow="clipboard-read; clipboard-write" style="border: none;"></iframe>
<h2 id="sharing-%26-embedding-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/flow-viewer/#sharing-%26-embedding-flows"></a> Sharing & Embedding Flows</h2>
<p>Any flow on <a href="https://flows.nodered.org/">flows.nodered.org</a> now has a <code>Share Flow</code> option in the <code>Actions</code> section on the right side of the flows page. Clicking this will provide you with an iframe like:</p>
<div style="position: relative" id="code-container-36">
<pre class="language-html"><code id="code-36" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span><br /> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://flows.nodered.org/flow/7c2dd3ccde70746a40ef8f5aa58c591c/share<span class="token punctuation">"</span></span><br /> <span class="token attr-name">allow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clipboard-read; clipboard-write<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">border</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-36" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Which you can paste/embed into any website or blog post. Nick has also <a href="https://discourse.nodered.org/t/previewing-flows-on-the-flow-library/">enabled the Node-RED forums to support these embeds too</a>, and is also how we've embeded the above flows too.</p>
<p>If you want more control over the sizing of the viewer, you can also include a <code>?height=</code> query parameter on the <code>src</code> value of the <code>iframe</code>. You may also need to hardcode the <code>height</code> property of the <code>iframe</code> itself to account for this change, depending on where you're embedding it to. For example:</p>
<div style="position: relative" id="code-container-43">
<pre class="language-html"><code id="code-43" class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>iframe</span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100%<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>250px<span class="token punctuation">"</span></span><br /> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://flows.nodered.org/flow/7c2dd3ccde70746a40ef8f5aa58c591c/share?height=100<span class="token punctuation">"</span></span><br /> <span class="token attr-name">allow</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clipboard-read; clipboard-write<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">border</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>iframe</span><span class="token punctuation">></span></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-43" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/Modernize your legacy industrial data. Part 2.Explore strategies, challenges, and smart processing techniques for enhancing legacy industrial data utilization in the IIoT era with Node-RED.2023-09-17T00:00:00ZSteve McLaughlin<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/">part 1 of this series</a>, 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 <code>node-red-contrib-buffer-parser</code> node can help.</p>
<p>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.</p>
<!--more-->
<h2 id="obtaining-industrial-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#obtaining-industrial-data"></a> Obtaining Industrial Data</h2>
<p>In order to convert the legacy data to a format more suited to IIoT we first need to <em>grab</em> that data.</p>
<p>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, <code>node-red-contrib-modbus</code>, <code>node-red-contrib-s7comm</code>, <code>node-red-contrib-omron-fins</code>, <code>node-red-contrib-mcprotocol</code>, <code>node-red-contrib-df1</code> and <code>node-red-contrib-cip-st-ethernet-ip</code> are just some of the PLC data access nodes available.</p>
<p>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...</p>
<h2 id="data-consistency" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#data-consistency"></a> Data consistency</h2>
<p>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.</p>
<p>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:</p>
<table>
<thead>
<tr>
<th>Register</th>
<th>Value</th>
<th>Description</th>
<th>Data Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>1, 2</td>
<td>10</td>
<td>Part Count</td>
<td>UINT32</td>
</tr>
<tr>
<td>3, 4</td>
<td>2.5</td>
<td>Cycle Time (sec)</td>
<td>UINT32/100</td>
</tr>
<tr>
<td>5, 6</td>
<td>30</td>
<td>Production Time (sec)</td>
<td>UINT32/100</td>
</tr>
<tr>
<td>7, 8</td>
<td>20</td>
<td>Run Time (sec)</td>
<td>UINT32/100</td>
</tr>
<tr>
<td>9, 10</td>
<td>11</td>
<td>Stoppage Time (sec)</td>
<td>UINT32/100</td>
</tr>
</tbody>
</table>
<p>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.</p>
<h3 id="why-is-the-data-inconsistent%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#why-is-the-data-inconsistent%3F"></a> Why is the data inconsistent?</h3>
<p>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.</p>
<p>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:</p>
<p><em>Image 1: individual reads</em>
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1-Rg1l2Ra-Yu-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1-Rg1l2Ra-Yu-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="image showing 10 individual reads" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1-Rg1l2Ra-Yu-650.jpeg" width="650" height="466" /></picture></p>
<p>That is a lot of nodes and a lot of duplication!</p>
<p>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:</p>
<p><em>Image 2: individual reads with data processing</em>
<picture><source type="image/gif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-10polls-6_DbG3YIuA-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Image showing 10 individual reads with data processing" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-10polls-6_DbG3YIuA-650.webp" width="650" height="431" /></picture></p>
<p>Yippie! We have the data, it works, we publish it to MQTT, job done. Right?</p>
<p>Unfortunately, no. The data is inconsistent.</p>
<h3 id="so-whats-the-big-deal%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#so-whats-the-big-deal%3F"></a> So whats the big deal?</h3>
<p>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!</p>
<p>Not only that, from a developer or maintainers perspective, it has many problems:</p>
<ol>
<li>Lots of error prone manual configuration (yes, I made several mistakes while creating the example)</li>
<li>Hard coded register addresses</li>
<li>Duplication</li>
<li>Inextensible - what if we need to read more registers?</li>
<li>Inconsistent data - as discussed above</li>
<li>Slow - each read takes time</li>
<li>Inefficient use of network bandwidth - each read requires a request and response packet</li>
</ol>
<h3 id="how-can-we-make-the-data-consistent%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#how-can-we-make-the-data-consistent%3F"></a> How can we make the data consistent?</h3>
<p>The most obvious solution is to stop the PLC before reading the data.
However, I am faily certain your boss will not be super pleased with stopping the manufacturing process.
The 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.</p>
<p>The next best thing to do is to read relative data as quickly as possible and in one block.</p>
<h3 id="a-quick-side-bar-(timing-is-...-everything)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#a-quick-side-bar-(timing-is-...-everything)"></a> A quick side-bar (timing is ... everything)</h3>
<p>In many protocols, including Modbus, data must be polled. Each poll, depending on many factors, can take a number of milliseconds.</p>
<p>Lets say, on a relatively quiet serial network, a single register poll takes 25 milliseconds (assume 9600 baud, 1 byte/ms, request packet size 8 bytes, response packet size 7 bytes, 10ms latency for the PLC to receive, process and respond to the request)</p>
<ul>
<li>If we read 10 registers individually, then the time taken to read the 10 registers is 250 milliseconds.</li>
<li>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.</li>
</ul>
<p>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.</p>
<p><em>Image 3: A comparison of individual reads vs block reads</em>
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-table-1ZaRnlRt2W-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-table-1ZaRnlRt2W-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="A data table comparing polls vs block read" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-table-1ZaRnlRt2W-650.jpeg" width="650" height="118" /></picture></p>
<p>OK, back to the topic at hand.</p>
<h2 id="getting-the-data-in-a-more-consistent-way" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#getting-the-data-in-a-more-consistent-way"></a> Getting the data in a more consistent way</h2>
<p>Here is the same example as above but this time we are reading the data in one go:</p>
<p><em>Image 4: Getting data in one block</em>
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1b-YS5YTJEG-Y-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1b-YS5YTJEG-Y-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="image showing 1 read for 10 registers" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-demo1b-YS5YTJEG-Y-650.jpeg" width="650" height="348" /></picture></p>
<p>Note how much simpler this is? Not only that, it is easier to maintain, faster, more extensible and most importantly, the data is consistent.</p>
<p>Great, lets move on.</p>
<h2 id="processing-the-data-in-readiness-for-iiot%3A-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#processing-the-data-in-readiness-for-iiot%3A-mqtt"></a> Processing the data in readiness for IIoT: MQTT</h2>
<p>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 <code>node-red-contrib-buffer-parser</code> we can easily convert the data into more meaningful formats.</p>
<p>The first, instinctive approach is to fan out the data and process it individually:</p>
<p><em>Image 5: block reads, individual processing</em>
<picture><source type="image/gif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-1poll-fixed-8MQZ8FpStS-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="image showing 1 modbus poll with individual processing" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-1poll-fixed-8MQZ8FpStS-650.webp" width="650" height="446" /></picture></p>
<p>This may be fine for a handful of registers but it soon becomes unwieldy and unmaintainable.</p>
<p>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:</p>
<p><em>Image 6: block reads, smart processing, no-code solution</em>
<picture><source type="image/gif" srcset="https://flowfuse.com/img/industrial-legacy-data-pt2-1poll-extensible-wws3PDBqV--650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="image showing 1 modbus poll with smart processing" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-pt2-1poll-extensible-wws3PDBqV--650.webp" width="650" height="455" /></picture></p>
<h2 id="node-red-in-production" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#node-red-in-production"></a> Node-RED in Production</h2>
<p>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.</p>
<p>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 <a href="https://flowfuse.com/docs/user/snapshots/">snapshots</a>, team collaboration tools, one-click deployment, and <a href="https://flowfuse.com/docs/user/user-settings/#security">multi-factor authentication</a>, FlowFuse streamlines the process and enhances the operational capabilities of Node-RED in production settings.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and experience FlowFuse's features</strong></p>
<h2 id="wrap-up" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data-part2/#wrap-up"></a> Wrap up</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/tulip-event-report/Tulip Operation Calling Event ReportThoughts and insighs from Tulip's recent customer event2023-09-14T00:00:00Z<p>This week I attended the Tulip event called <a href="https://www.operationscalling.com/">Operations Calling</a> in Boston. Here are some quick observations from the event.</p>
<!--more-->
<p>For those that might not know <a href="https://tulip.co/">Tulip</a>, they are a Boston based company focused on frontline operations in the manufacturing and industrial automation industry. They provide the software to help factory workers be more efficient at their job. Some people refer to this category as MES but Tulip avoids this term since MES can carry a lot of negative baggage from legacy MES vendors.</p>
<p>Some quick thoughts on the event:</p>
<ul>
<li>I would estimate about 300 people attended the event. It was held at Tulip’s office, which is actually in an old Ford factory. The offices and environment were very cool.</li>
<li>There was a large emphasis on Tulip partners and the ecosystem. Tulip knows it needs to be open and play well with others. There were 27 different vendors showing in their partner pavillion. Pretty impressive given the size of Tulip.</li>
<li>Tulip has a no-code environment for setting up their software. However, they realize you also need low-code, like Node-RED, for integrating hardware and software into Tulip. I attended two sessions that discussed how Node-RED is an important part of the Tulip integration and customization strategy. Node-RED was also being used in some of their demos stations.</li>
<li>Tulip talks about ‘citizen developers’ and ‘composable apps’. This is the future of manufacturing. Organizations need to start thinking in terms of smaller applications that do a specific task that can also be rolled up into something bigger at a later time. These small applications need to be developed by the people closest to the problem, not the IT team or a system integrator.</li>
<li>During the closing panel, it was stated that ‘Today, Excel is the most popular digital automation platform used in industry.’ There are a lot of spreadsheets being used to collect and visualize data. However, the industry needs to find a better way. Node-RED is an important part of the solution. The people who can run Excel spreadsheets can easily use Node-RED. However, using Node-RED will let them do even more than what Excel allows them to do.</li>
</ul>
<p>Operation Calling was definitely an event worth attending and I hope to return next year.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/Modernize your legacy industrial dataWorking with legacy industrial protocol data from the likes of Modbus and older, non IIoT protocols and putting it to work in an IIoT world.2023-09-14T00:00:00ZSteve McLaughlin<p>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).</p>
<p>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.</p>
<!--more-->
<h3 id="legacy-industrial-data%3A-modbus" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#legacy-industrial-data%3A-modbus"></a> Legacy Industrial Data: Modbus</h3>
<p>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.</p>
<h3 id="a-short-primer-on-data-types" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#a-short-primer-on-data-types"></a> A short primer on data types</h3>
<p>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.</p>
<h4 id="16-bit-unsigned" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#16-bit-unsigned"></a> 16-bit unsigned</h4>
<p>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 <code>0x3039</code> in hexadecimal or <code>0011000000111001</code> in binary.</p>
<h4 id="16-bit-signed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#16-bit-signed"></a> 16-bit signed</h4>
<p>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 <code>0xCFC7</code> in hexadecimal or <code>1100111111000111</code> in binary.</p>
<h4 id="32-bit-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#32-bit-data"></a> 32-bit data</h4>
<p>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 <code>0x00003039</code> in hexadecimal or <code>00000000000000000011000000111001</code> 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.</p>
<h4 id="endianness" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#endianness"></a> Endianness</h4>
<p>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 <code>0x3039</code> in a big-endian word and <code>0x3930</code> in little-endian word. This can often cause confusion and complicate the process of converting Modbus data into more usable formats.</p>
<h3 id="node-red-and-node-red-contrib-buffer-parser-to-the-rescue" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#node-red-and-node-red-contrib-buffer-parser-to-the-rescue"></a> Node-RED and node-red-contrib-buffer-parser to the rescue</h3>
<p>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, <code>node-red-contrib-buffer-parser</code>, provides a solution to the legacy data conversion challenge.</p>
<p>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 <code>node-red-contrib-modbus</code> outputs), and convert it into various data types. It can output pretty much any data type, including byte-swapped data, WORD swapped data, masked/shifted/scaled data, and even individual bits.</p>
<p>Here's a quick overview of how it works:</p>
<ol>
<li>
<p><strong>Data Parsing</strong>: 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.</p>
</li>
<li>
<p><strong>Data Conversion</strong>: 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.</p>
</li>
<li>
<p><strong>Publishing to MQTT, influxDB, a dashboard, an IIoT system</strong>: 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.</p>
</li>
</ol>
<h3 id="unlocking-the-potential-of-legacy-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#unlocking-the-potential-of-legacy-data"></a> Unlocking the Potential of Legacy Data</h3>
<p>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.</p>
<p>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!</p>
<h3 id="3-quick-demos-of-node-red-and-the-buffer-parser-node-in-action" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#3-quick-demos-of-node-red-and-the-buffer-parser-node-in-action"></a> 3 quick demos of Node-RED and the buffer parser node in action</h3>
<p>Here are 3 quick demonstrations that barely scratch the surface of possibilities:</p>
<h4 id="example-1%3A-modbus-to-mqtt" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#example-1%3A-modbus-to-mqtt"></a> Example 1: Modbus to MQTT</h4>
<p>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!
<picture><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-to-mqtt-JkpUlp_dyS-650.webp 650w, https://flowfuse.com/img/industrial-legacy-data-to-mqtt-JkpUlp_dyS-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/gif" srcset="https://flowfuse.com/img/industrial-legacy-data-to-mqtt-JkpUlp_dyS-650.gif 650w, https://flowfuse.com/img/industrial-legacy-data-to-mqtt-JkpUlp_dyS-1300.gif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Legacy data to MQTT" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-to-mqtt-JkpUlp_dyS-650.webp" width="1300" height="310" /></picture></p>
<h4 id="example-2%3A-modbus-to-influxdb" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#example-2%3A-modbus-to-influxdb"></a> Example 2: Modbus to InfluxDB</h4>
<p>Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to influxDB!
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-650.avif 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-650.webp 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-650.jpeg 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Legacy data to influxDB" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-to-influx-4f0_LkwORM-650.jpeg" width="1300" height="268" /></picture>
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-650.avif 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-650.webp 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-650.jpeg 650w, https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Legacy data to influxDB2" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-to-influx2-MUgdhB-Lfz-650.jpeg" width="1300" height="692" /></picture></p>
<h4 id="example-3%3A-modbus-data-on-a-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#example-3%3A-modbus-data-on-a-dashboard"></a> Example 3: Modbus data on a dashboard</h4>
<p>Converting an array of 16-bit unsigned integers to String, Float and a scaled integer for publishing to a dashboard!
<picture><source type="image/avif" srcset="https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-650.avif 650w, https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-650.webp 650w, https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-650.jpeg 650w, https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Legacy data to dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/industrial-legacy-data-to-dashboard-85RdTa3-n6-650.jpeg" width="1300" height="687" /></picture></p>
<h3 id="simplify-your-node-red-operations-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#simplify-your-node-red-operations-with-flowfuse"></a> Simplify Your Node-RED Operations with FlowFuse</h3>
<p>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.</p>
<p>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 <a href="https://flowfuse.com/product/features/">features</a> designed to streamline your Node-RED workflows and boost overall performance.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and experience FlowFuse's features</strong></p>
<h3 id="learn-more" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/modernize-your-legacy-industrial-data/#learn-more"></a> Learn More</h3>
<p>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:</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/node-red/">Node-RED blog posts</a></li>
<li><a href="https://www.youtube.com/playlist?list=PLpcyqc7kNgp09XeRx_cae1fEIOloPqM1C">Node-RED videos</a></li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-buffer-parser">Buffer Parser Node</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/Community News September 2023Your monthly update for the FlowFuse and Node-RED communities2023-09-09T00:00:00Z<p>Welcome to the FlowFuse newsletter for September 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community.</p>
<h2 id="new-name" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#new-name"></a> New Name</h2>
<p>The big news this month is that <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/">FlowForge is now FlowFuse</a>. Yes, we have changed our name but we are still focus on delivering great products for the Node-RED community.</p>
<!--more-->
<h2 id="new-releases" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#new-releases"></a> New Releases</h2>
<ul>
<li>Node-RED 3.1 has been <a href="https://nodered.org/blog/2023/09/06/version-3-1-released">released</a>. Among the changes are Mermaid chart support, locking flows, and much more.</li>
<li>Last week, FlowFuse 1.11 was released. This release included personal access tokens for FlowFuse API and new tiers for FlowFuse Cloud, including a new starter tier. Check out the details in our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/">announcement</a>.</li>
</ul>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#upcoming-events"></a> Upcoming events</h2>
<h3 id="celebrate-10-years-of-node-red-and-what%E2%80%99s-new-in-3.1-and-beyond" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#celebrate-10-years-of-node-red-and-what%E2%80%99s-new-in-3.1-and-beyond"></a> Celebrate 10 Years of Node-RED and What’s New in 3.1 and Beyond</h3>
<p>Hard to believe that Node-RED was launched 10 years ago. We want to celebrate this great accomplishment and also show off the new Node-RED 3.1 release. Join Nick O'Leary for the 10 year celebration and here what is coming next.</p>
<p><a href="https://flowfuse.com/webinars/2023/node-red-10-years/">Sign-up today</a> to join us on September 21.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#from-our-blog"></a> From our Blog</h2>
<ul>
<li>
<p>The Node-RED Dashboard 2.0 project is making excellent progress. Two updates were published in the last month. Make sure you check out the latest release and provide feedback.</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/">Dynamic Markdown, Tables & Notebooks with Dashboard 2.0</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/">Dashboard 2.0 - Community Update</a></li>
</ul>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/bosch-rexroth-announce/">FlowFuse announces a Node-RED stack for Industry 4.0 applications on Bosch Rexroth ctrlX Automation</a> - a fully supported Node-RED stack for Bosch customers.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/aws-marketplace-announce/">FlowFuse is now available on the AWS Marketplace</a> - making it easier to run FlowFuse on the AWS cloud.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/open-source-is-a-tier-not-competition/">Our Open Source offering is a tier, not our competition</a> - some insight from our CEO into our open source strategy.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/">Why the Automation Pyramid blocks digital transformation - The Role of Unified Namespace</a> - some insight from the FlowFuse product manager into the role of a Unified Namespace.</p>
</li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#from-the-community"></a> From the Community</h2>
<ul>
<li>A new plugin for the generation of <a href="https://flows.nodered.org/node/@node-red-matter/node-red-matter">Matter devices witin Node-RED</a> has been published.</li>
</ul>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/community-news-09/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4958271004">Developer Relations Engineer - Manufacturing & Industrial Automation</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/bosch-rexroth-announce/FlowFuse announces a Node-RED stack for Industry 4.0 applications on ctrlX AUTOMATIONRexroth ctrlX now have fully supported Node-RED stack available for production use2023-09-07T00:00:00Z<p>FlowFuse is pleased to announce they are now offering Node-RED plus select third party nodes from the Bosch Rexroth ctrlX World marketplace.</p>
<!--more-->
<p>FlowFuse is pleased to announce they are now offering Node-RED plus select third party nodes from the <a href="https://developer.community.boschrexroth.com/t5/Store-and-How-to/FlowFuse-Node-RED/ba-p/82135">Bosch Rexroth ctrlX Store</a> marketplace. Rexroth customers building Industry 4.0 applications will now have a trusted vendor, in FlowFuse, to provide support and updates for using open source Node-RED in production. By partnering with FlowFuse, customers can reduce their risk of using Node-RED in production by relying upon FlowFuse Node-RED experts to assist with any development or production issues.</p>
<p><a href="https://nodered.org/">Node-RED</a> is a popular open source low-code development environment widely used in industries for collecting and processing industrial data to deliver Industry 4.0 applications. FlowFuse is uniquely positioned to partner with ctrlX customers looking to use Node-RED in production. FlowFuse CTO Nick O’Leary is the co-creator and project leader of Node-RED. FlowFuse employs many Node-RED experts who have years of experience helping customers with successful deployment of Node-RED applications.</p>
<p>The FlowFuse package offered in the ctrlX World marketplace will provide ctrlX customers the following benefits:</p>
<ul>
<li>Support for Node-RED development and production deployments</li>
<li>Support for third party nodes of popular industrial protocols including: Modbus, OMRON, S7 and MC Protocol.</li>
</ul>
<p>FlowFuse is the only vendor providing professional technical support for Node-RED on the ctrlX platform.</p>
<p>The package is available today at ctrlX World. Interested customers should contact their Rexroth sales representative for purchasing details. Interested customers can also contact FlowFuse directly at <a href="mailto:sales@flowfuse.com/">sales@flowfuse.com</a> for additional information about the offering.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/Dynamic Markdown, Tables & Notebooks with Dashboard 2.0A dive into the new features available in 0.4.0 - The "Notebook" Layout and new dynamic Markdown & Table widgets.2023-09-06T00:00:00ZJoe Pavitt<p>Whilst we're still busy backporting through the existing Dashboard 1.0 features, we did want to highlight some new features we've built in Dashboard 2.0 released this week.</p>
<!--more-->
<p>In our v0.4.0 release, we've introduced a new "Notebook" layout, alongside a new Table widget.</p>
<p>The Notebook layout is designed to allow users to create Dashboards structured like a Notebook (most often seen with the likes of <a href="https://jupyter.org/">Jupyter Notebooks</a> or <a href="https://observablehq.com/">ObservableHQ</a>).</p>
<p>Here we will deepdive into the Notebook layout, and show how, alongside our new <strong>Markdown Node</strong> (<a href="https://dashboard.flowfuse.com/nodes/widgets/ui-markdown.html">docs</a>), <strong>Table Node</strong> (<a href="https://dashboard.flowfuse.com/nodes/widgets/ui-table.html">docs</a>) and others, it's becoming easier to create dynamic and interactive Dashboards.</p>
<p><em>Note: If you're not familiar with Markdown, it's a simple markup language that allows you to format text. You can learn more about it <a href="https://www.markdownguide.org/cheat-sheet/">here</a>.</em></p>
<h2 id="dashboard-hierarchy" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/#dashboard-hierarchy"></a> Dashboard Hierarchy</h2>
<p>As a quick introductory note ahead of our below guide, each Dashboard is structured accordingly:</p>
<ul>
<li><strong>Widget</strong>: An individual functional block, e.g. button, chart, slider</li>
<li><strong>Group</strong>: A collection of widgets that render together</li>
<li><strong>Page</strong>: A single page/tab in your Dashboard. Each page can have it's own Layout, in this case we'll use "Notebook"</li>
<li><strong>UI</strong>: Contains a collection of pages, deployed from Node-RED, provides the basic side navigation to switch between Pages.</li>
</ul>
<h2 id="building-a-notebook" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/#building-a-notebook"></a> Building a Notebook</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-650.avif 650w, https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-650.webp 650w, https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-650.jpeg 650w, https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Example Notebook created in Dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/db-notebook-example-owO0_T3fAc-650.jpeg" width="1300" height="748" /></picture></p>
<p>To get started, drop your first widget (in this case, we'll add a <code>ui-markdown</code>) onto the Node-RED canvas. This in turn will prompt us to create our first Group/Page/Dashboard which we can name and configure accordingly.</p>
<p>Let's add the following Markdown to our first widget:</p>
<div style="position: relative" id="code-container-56">
<pre class="language-md"><code id="code-56" class="language-md"><span class="token title important"><span class="token punctuation">#</span> Markdown Content</span><br /><br />Here we can render dynamic Markdown content that is<br />easily <span class="token italic"><span class="token punctuation">_</span><span class="token content">styled</span><span class="token punctuation">_</span></span>.<br /><br /><br />We can inject <span class="token code-snippet code keyword">`msg.payload`</span>. For example, here is a<br />timestamp updating every second: {{ msg.payload }}<br /></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-56" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>The joy of <code>ui-markdown</code> in Dashboard 2.0 is <em>dynamic</em> content, i.e. content that can be updated by passing messages to the <code>ui-markdown</code> node. We can wire an <code>inject</code> node, set it up to repeat every second, and connect it to <code>ui-markdown</code>. Now, our Markdown content will automatically update show this value.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-650.avif 650w, https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-650.webp 650w, https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-650.jpeg 650w, https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot to show how an inject node can drive content of a ui-markdown node" loading="lazy" decoding="async" src="https://flowfuse.com/img/db-notebook-inject-II6FdXD8Rs-650.jpeg" width="1300" height="919" /></picture></p>
<p>Resulting in:</p>
<p><picture><source type="image/webp" srcset="https://flowfuse.com/img/md-timestamp-xxFVIvAA41-650.webp 650w, https://flowfuse.com/img/md-timestamp-xxFVIvAA41-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/gif" srcset="https://flowfuse.com/img/md-timestamp-xxFVIvAA41-650.gif 650w, https://flowfuse.com/img/md-timestamp-xxFVIvAA41-1300.gif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Dynamic markdown with an updating timestamp every 1 second" loading="lazy" decoding="async" src="https://flowfuse.com/img/md-timestamp-xxFVIvAA41-650.webp" width="1300" height="314" /></picture></p>
<h2 id="adding-more-widgets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/#adding-more-widgets"></a> Adding More Widgets</h2>
<p>Because the Notebook is <em>just</em> a layout, we can still wire together any of the available widgets and existing nodes and display them accordingly.</p>
<p>Let's wire a <code>ui-button</code>, HTTP Request, and <code>ui-table</code> node. When we click the button, it will perform the HTTP request, and then render the response in the table.</p>
<p>For this, we're going to use the Random Jokes API, and in particular, a call to <code>https://official-joke-api.appspot.com/jokes/ten</code> which will return 10 random jokes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/generate-jokes-flow-p2CormG-_Z-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/generate-jokes-flow-p2CormG-_Z-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot showing a simple Button > HTTP Request > Table flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/generate-jokes-flow-p2CormG-_Z-650.jpeg" width="650" height="157" /></picture></p>
<p>We can also re-order the widgets on the page using the Dashboard 2.0 sidebar (as you could in Dashboard 1.0).</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/db-notebook-order-IlMATyXnac-650.avif 650w, https://flowfuse.com/img/db-notebook-order-IlMATyXnac-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/db-notebook-order-IlMATyXnac-650.webp 650w, https://flowfuse.com/img/db-notebook-order-IlMATyXnac-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/db-notebook-order-IlMATyXnac-650.jpeg 650w, https://flowfuse.com/img/db-notebook-order-IlMATyXnac-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot to show how an inject node can drive content of a ui-markdown node" loading="lazy" decoding="async" src="https://flowfuse.com/img/db-notebook-order-IlMATyXnac-650.jpeg" width="1300" height="1210" /></picture></p>
<p>The above effort results in the following output in our Notebook:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-650.avif 650w, https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-650.webp 650w, https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-650.jpeg 650w, https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot to show how an inject node can drive content of a ui-markdown node" loading="lazy" decoding="async" src="https://flowfuse.com/img/db-notebook-jokes-table-TTd0Taui34-650.jpeg" width="1300" height="1150" /></picture></p>
<h2 id="what-else-is-new-in-0.4.0%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/09/dashboard-notebook-layout/#what-else-is-new-in-0.4.0%3F"></a> What else is new in 0.4.0?</h2>
<p>The above demonstrates just a few of the new features in the 0.4.0 Release, but we've also added <a href="https://github.com/FlowFuse/node-red-dashboard/releases/tag/v0.4.0">other fixes and improvements</a>. In particular, I want to call out Steve's great work on implementing custom class injection, the first of our new <a href="https://dashboard.flowfuse.com/user/dynamic-properties.html">"Dynamic Properties"</a>, of which there will be more (e.g. visibility, disabled, etc.) to come.</p>
<p>As always, thanks for reading and your interested in Dashboard 2.0. If you have any feature requests, bugs/complaints or general feedback, please do reach out, and raise issues on our relevant <a href="https://github.com/FlowFuse/node-red-dashboard">GitHub repository</a>.</p>
<ul>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/1">Dashboard 2.0 Activity Tracker</a></li>
<li><a href="https://github.com/orgs/FlowFuse/projects/15/views/4">Dashboard 2.0 Planning Board</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/FlowFuse Cloud Starter package - The easiest way to get started with Node-REDIntroducing tiers and pricing changes for FlowFuse Cloud2023-08-31T00:00:00ZNick O'Leary<p>FlowFuse is used by businesses and hobbyists alike. We aim to provide a platform that meets the needs of both, recognizing the importance of hobbyists in the Node-RED community and the long term success of FlowFuse as a company. We want to make it as easy as possible for people to get started on FlowFuse Cloud and help them understand the full value the platform offers. One of the barriers we've seen has been the current pricing structure of FlowFuse Cloud.</p>
<!--more-->
<p>Since FlowFuse Cloud was introduced there’s been essentially one tier for everyone to adopt. Customers were charged $15 per month for each Node-RED instance managed by the FlowFuse platform, whether running in the cloud or on their own hardware using our Device Agent.</p>
<p>Today we’re introducing different tiers: Starter and Team. The Starter tier gives you access to 2 FlowFuse managed Node-RED instances, 2 Node-RED devices, with up to 2 team members - all for a flat rate of $15 per month.</p>
<p>The new Team tier unlocks more value to adopt FlowFuse in a professional environment and grow beyond the Starter tier. It’s well suited for teams up to 20 members, and includes increased limits for file storage and persistent context. It also unlocks some key features such as the shared Team Library and Project Nodes, used to seamlessly transport data between instances. Pricing starts at $25 per managed Node-RED instance and $15 per device.</p>
<p>We will soon be launching our third tier, more targeted at Enterprise users. This tier enhances the compliance capabilities through Single-Sign on (SSO), and provides higher SLA’s for both support and high availability for running Node-RED at scale.</p>
<p>We expect these layers to fit the customers as they grow in their FlowFuse and Node-RED adoption journey.</p>
<h2 id="faq" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#faq"></a> FAQ</h2>
<h3 id="do-you-have-an-overview-of-the-different-tiers%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#do-you-have-an-overview-of-the-different-tiers%3F"></a> Do you have an overview of the different tiers?</h3>
<p>Certainly, an overview of the tiers and prices are available at our <a href="https://flowfuse.com/pricing/">pricing page</a>.</p>
<h3 id="how-do-i-upgrade-from-starter-to-team-tier%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#how-do-i-upgrade-from-starter-to-team-tier%3F"></a> How do I upgrade from Starter to Team tier?</h3>
<p>You can update your existing team to either tier via the ‘Change Team Type’ option under Team Settings.</p>
<h3 id="how-do-i-migrate-my-existing-team-to-the-newly-introduced-starter-package-or-team-tier%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#how-do-i-migrate-my-existing-team-to-the-newly-introduced-starter-package-or-team-tier%3F"></a> How do I migrate my existing team to the newly introduced Starter package or Team tier?</h3>
<p>You can update your existing team to either tier via the ‘Change Team Type’ option under Team Settings on the "Danger" tab.</p>
<h3 id="how-do-i-cancel-the-starter-package%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#how-do-i-cancel-the-starter-package%3F"></a> How do I cancel the Starter Package?</h3>
<p>Once you have deleted any instances, applications or devices in the team, you can delete the Team itself under the Team Settings section.</p>
<h3 id="i%E2%80%99m-self-hosting-flowfuse---do-these-changes-affect-me%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#i%E2%80%99m-self-hosting-flowfuse---do-these-changes-affect-me%3F"></a> I’m self-hosting FlowFuse - do these changes affect me?</h3>
<p>No, the new team tiers and pricing are on FlowFuse Cloud only.</p>
<h3 id="i%E2%80%99m-using-the-digitalocean%2Faws-marketplace-version---do-these-changes-affect-me%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#i%E2%80%99m-using-the-digitalocean%2Faws-marketplace-version---do-these-changes-affect-me%3F"></a> I’m using the DigitalOcean/AWS Marketplace version - do these changes affect me?</h3>
<p>No, the new team tiers and pricing are on FlowFuse Cloud only.</p>
<h3 id="anything-else%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/new-starter-tier/#anything-else%3F"></a> Anything else?</h3>
<p>If you have any further questions, please <a href="https://flowfuse.com/contact-us/">contact us</a> and we’ll be happy to discuss.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/FlowFuse 1.11 makes it easier to get started with FlowFuse and Node-REDOur latest release includes a new starter tier for FlowFuse Cloud, Personal Access Tokens for API access and improvements to device management.2023-08-31T00:00:00Z<p>FlowFuse 1.11 introduces a new starter tier for FlowFuse Cloud that makes it easier to get started with FlowFuse and Node-RED.</p>
<!--more-->
<h2 id="new-flowfuse-cloud-starter-tier-%232328" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#new-flowfuse-cloud-starter-tier-%232328"></a> New FlowFuse Cloud Starter Tier <a href="https://github.com/FlowFuse/flowfuse/issues/2328">#2328</a></h2>
<p>It is now easier for Node-RED developers to get started with FlowFuse and Node-RED. The new starter tier allows developers to use two Node-RED instances and two remote device deployments. Ideal for creating proof of concepts or running a home automation system with Node-RED.</p>
<p>FlowFuse provides a cloud hosted version of Node-RED so developers don't need to worry about Node-RED installation or operation. This makes it a lot easier to get started with Node-RED and easier to maintain a running instance.</p>
<h2 id="flowfuse-api-access-now-possible-via-personal-access-tokens-%2314" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#flowfuse-api-access-now-possible-via-personal-access-tokens-%2314"></a> FlowFuse API access now possible via personal access tokens <a href="https://github.com/FlowFuse/flowfuse/issues/14">#14</a></h2>
<p>FlowFuse APIs are now accessible via personal access tokens (PAT). This makes it possible to create automation scripts that interact with the FlowFuse platform using the API and authenticate the scripts with the PAT.</p>
<h2 id="usability-improvements-to-device-management-%232294" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#usability-improvements-to-device-management-%232294"></a> Usability Improvements to Device Management <a href="https://github.com/FlowFuse/flowfuse/issues/2334">#2294</a></h2>
<p>A number of usability improvements have been added to the FlowFuse device management solution to make it more flexible and intuitive to use. These improvements include being able to associate devices at the application level allowing for easier editing of Node-RED instances on edge devices.</p>
<h2 id="flowfuse-rebranding-%23119" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#flowfuse-rebranding-%23119"></a> FlowFuse Rebranding <a href="https://github.com/orgs/FlowFuse/projects/1?pane=issue&itemId=34719640">#119</a></h2>
<p>Earlier in August, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/">FlowForge announced</a> a change to our company and product name to FlowFuse. Work has begun to change the product branding to FlowFuse. The UI has been rebranded and the remaining points will be changed in the next release.</p>
<h2 id="other-new-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#other-new-features"></a> Other New Features</h2>
<ul>
<li>Add ability to add a description to an application and display it in the portal <a href="https://github.com/FlowFuse/flowfuse/issues/2279">#2279</a></li>
<li>UI Improvements to device management <a href="https://github.com/FlowFuse/flowfuse/issues/2427">#2427</a></li>
<li>Improve landing page for documentation <a href="https://github.com/FlowFuse/website/issues/842">#842</a></li>
<li>Restructure of user interface navigation <a href="https://github.com/FlowFuse/flowfuse/issues/2474">#2474</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#bug-fixes"></a> Bug Fixes</h2>
<ul>
<li>Device running old snapshot <a href="https://github.com/FlowFuse/device-agent/issues/132">#132</a></li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.11.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowfuse-1-11-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go to the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/aws-marketplace-announce/FlowFuse is now available on AWS MarketplaceMaking is easier to run Node-RED and FlowFuse on AWS Cloud2023-08-21T00:00:00Z<p>Many customers want to run FlowFuse in their own cloud environment, AWS being a great example. Today we're excited to announce that FlowFuse is now available from the <a href="https://aws.amazon.com/marketplace/pp/prodview-3ycrknfg67rug?sr=0-1&ref_=beagle&applicationId=AWSMPContessa">AWS Marketplace</a>. This makes it very easy for customers to install and run Node-RED and FlowFuse within minutes.</p>
<!--more-->
<p>FlowFuse allows organizations to reliably deliver Node-RED applications in a continuous, collaborative and secure manner. Customers running FlowFuse on AWS Cloud will benefit from FlowFuse's features, including:</p>
<ul>
<li>Team collaboration for Node-RED developers, allowing multiple developers to work together on a single Node-RED instance, including the ability to have an audit log of changes.</li>
<li>DevOps deliver pipelines that support a software development lifecycle for Node-RED development. Pipelines can be setup to establish development, test and production environments for Node-RED instances.</li>
<li>Snapshot of Node-RED instances to create a version history of changes to Node-RED applications. This also includes the ability to rollback to a previous version of a Node-RED instance.</li>
</ul>
<p>FlowFuse on the AWS Marketplace is available under the Apache License c2.0 open source license. Customers can use FlowFuse free of charge but will need to pay AWS EC2 usage for hosting FlowFuse. FlowFuse offers commercially licensed Premimum and Enterprise tiers that includes enterprise oriented features, including:</p>
<ul>
<li>24/7 technical support</li>
<li>Single Sign-on</li>
<li>High Availability for Node-RED applications</li>
<li>Remote device management</li>
</ul>
<p>Customers can easily upgrade to the premium or enterprise tier by obtaining a commercial license from FlowFuse. To understand what FlowFuse can do for your use-case, please <a href="https://flowfuse.com/book-demo/">book a demo</a>.</p>
<p>FlowFuse on AWS Marketplace is available <a href="https://aws.amazon.com/marketplace/pp/prodview-3ycrknfg67rug?sr=0-1&ref_=beagle&applicationId=AWSMPContessa">immediately</a>. Please refer to our <a href="https://flowfuse.com/docs/install/docker/aws-marketplace/#installing-flowfuse-from-aws-market-place">documentation</a> and give it a try and let us know what you think.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/open-source-is-a-tier-not-competition/Our Open Source offering is a tier, not our competitionDiscover the perspective on open source as a valuable tier, not competition.2023-08-18T00:00:00ZZJ van de Weg<p>More than once we’ve been in discussion with prospective customers on what tier
is the right tier for their current Node-RED adoption. The question is likely to
come up "Why wouldn't we just use the open source version of FlowFuse?". The
implicit discussion created is one that is alike the question:
“Why wouldn’t we go with your competition?”. For FlowFuse, and most other open-core
companies like us, the open licensed and free to use core is tier, not competition.</p>
<!--more-->
<p>In the traditional sense, the prospective customer is right. By the definition,
a customer <strong>buys</strong> goods or services by exchanging it for <strong>money</strong>. For
FlowFuse’s open-source edition, which is free as in beer and free as in speech,
no money changes hands. It can be installed and run by anyone. Once the software
is being installed and used, we consider we've gained a new customer.
There’s an agreement in place, the Apache 2.0 license, and value is obtained by
the customer. The only missing component compared to an ‘ordinary’ sale is the lack
of money from the customer to FlowFuse. The fact that no monetary value is exchanged,
like the situation where a customer picks the competitor, doesn’t make the open
source tier competition. It is just a free tier.</p>
<p>Another reason it's really a tier is that the core of the product is the same. In
many open-core products, the path to upgrade from the open source license product to
the paid tier is much alike customers are used to on SaaS models. In the reals of
self-managed software that's mostly uploading a license and at times a few configuration
steps.</p>
<p>Furthermore, the open tier is a tier as the customer choses to not adopt all
capabilities. They're leaving value on the table. Either this is because it's not
quite clear what the value is or if the higher tiers provides
enough business value to warrant the expense. Or the adoption journey for the
customer doesn't yet require the full featured tiers.</p>
<p>What’s unique about open source software, is that customers can exchange value
towards the company and community building the software in other forms: by
opening issues, updating documentation, advocating for the OSS variant, among
other ways. While this is not <strong>money</strong>, it is significant for a young company
like FlowFuse.</p>
<h2 id="challenges-with-a-open-core-free-tier" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/open-source-is-a-tier-not-competition/#challenges-with-a-open-core-free-tier"></a> Challenges with a open core free tier</h2>
<p>That's not to say that an open source tier is a silver bullet for a company. For
one, it's hard to track how many users a software package has, and who these users
are. For example; FlowFuse has <a href="https://flowfuse.com/docs/admin/telemetry/#usage-telemetry">Telemetry</a>,
though it can be turned off. Nor do we know who hosts this software.</p>
<p>Another challenge is around product and feature packaging. At FlowFuse and other open core companies it's
uncommon to move features from the open tier to paid tier only. If this choice has
been made it's a done deal, even when the product team got it wrong. Usually the
initial thoughts are therefor to move all features into the paid tiers. However,
this hampers long term growth as adoption of paid features are adopted later or
not at all. We follow the
<a href="https://opencoreventures.com/blog/2023-01-open-core-standard-pricing-model">Open-Core buyer based model</a>
to segregate the value, about which I'll write a post next time.</p>
<p>Photo by <a href="https://unsplash.com/@matthardy">Matt Hardy</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-is-now-flowfuse/FlowForge is now FlowFuseNew identity but same vision for industrial data integration2023-08-17T00:00:00ZZJ van de Weg<p>We are happy to announce that FlowForge is changing its name to FlowFuse. Changing our corporate identity wasn’t our top priority but a recent trademark challenge has promoted us to create a new brand for our company.</p>
<!--more-->
<p>We believe that this new name better reflects our core mission and aspirations. Just as electricity fuses elements to create energy, the FlowFuse platform fuses together data, ideas, processes, and technologies to generate a powerful force of innovation and transformation.</p>
<p>Selecting a new name for a company or product is never easy. Some of the requirements we set for a new name were: 1) it should be reasonably close to FlowForge so it will be easier to transition the brand identity, 2) it should even better reflect our mission, and 3) keeping the FF acronym will help with environment variable prefixes :-). I think we have accomplished all the above and am delighted to move forward as FlowFuse.</p>
<p><strong>What to Expect Next</strong></p>
<p>Our team is heads down building a platform to allow organizations to reliably deliver Node-RED applications in a continuous, collaborative, and secure manner. We'll continue to release regular updates packed with new features and enhancements that keeps moving us towards this goal.</p>
<p>A complete rebranding is a big piece of work - today marks the start of a process as we introduce the new FlowFuse name and updated <a href="https://flowfuse.com/">website</a>. Over the next number of days and weeks we'll continue to roll this change out, including our social media accounts and other accounts. The product branding will be changed over the next couple of releases, including FlowForge Cloud and FlowForge open source edition. The underlying software will be the as-if you’re running the next FlowForge release, though now called FlowFuse. We'll share more technical details of this when any changes are made.</p>
<p>We are excited by the future FlowFuse presents to our customers and the industry. Node-RED and FlowFuse is a powerful combination that gives access to industrial data to transform organizations and drive forward innovation across industries. Join us as we continue our journey.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/Dashboard 2.0 - Community UpdateOur latest Community Update for Dashboard 2.0, including the latest new widgets, fixes and updates on what's next.2023-08-10T00:00:00ZJoe Pavitt<p>Welcome to the latest Node-RED Dashboard 2.0 update. We've added lots of new widgets, cleaned up compatibility issues alongside Dashboard 1.0 and made strides to improve the events system linking the Node-RED editor with the Dashboard.</p>
<!--more-->
<p>I firstly need to begin with a <em>"Thank You"</em> to the dozens of pre-alpha users we've had so far. Thanks for being patient whilst we're shipping fast and breaking things. We've had some great feedback, and we're working hard to implement is as best as possible.</p>
<p>With all of the changes we've been making, we've also made the decision to jump to minor version numbers, and so, <strong>0.1.0 is available now</strong>.</p>
<p>Below you'll find a summary of the changes we've made since our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/">last community update</a>.</p>
<h2 id="new-widgets" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#new-widgets"></a> New Widgets</h2>
<h3 id="template-(docs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#template-(docs)"></a> Template (<a href="https://dashboard.flowfuse.com/nodes/widgets/ui-template.html" target="_blank">docs</a>)</h3>
<p>Steve has been doing some incredible work on the new <code>ui-template</code> widget. This widget allows you to create your own custom components using raw HTML, but also works with any of the components in the <a href="https://vuetifyjs.com/en/components/all/">Vuetify</a> component library. It's a powerful tool that will enable users to be creative with their own widgets that are not currently available with the standard set of widgets.</p>
<p><img alt="Examples of ui-template" loading="lazy" decoding="async" src="https://dashboard.flowfuse.com/images/node-examples/ui-template.png" width="undefined" height="undefined" /></p>
<p>The Template node also provides access to two built-in functions that can be used to send data back to Node-RED:</p>
<ul>
<li><strong>send(msg)</strong>: Outputs a message (defined by the input to this function call) from this node in the Node-RED flow.</li>
<li><strong>submit()</strong>: Send a <code>FormData</code> object when attached to a <code><form></code> element. The created object will consist of the <code>name</code> attributes for each form element, corresponding to their respective <code>value</code> attributes.</li>
</ul>
<h3 id="toggle-switch-(docs)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#toggle-switch-(docs)"></a> Toggle Switch (<a href="https://dashboard.flowfuse.com/nodes/widgets/ui-switch.html" target="_blank">docs</a>)</h3>
<p><img alt="Examples of ui-switch" loading="lazy" decoding="async" src="https://dashboard.flowfuse.com/images/node-examples/ui-switch.png" width="undefined" height="undefined" /></p>
<p>Adds a toggle switch to the user interface that can be rendered with a label, and traditional toggle switch, or, as in Dashboard 1.0, can be a square element with an icon & color provided.</p>
<h2 id="fixes-%26-other-changes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#fixes-%26-other-changes"></a> Fixes & Other Changes</h2>
<h3 id="sidebar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#sidebar"></a> Sidebar</h3>
<p>As requested on multiple occasions by the community when we released v0.0.4 of Dashboard 2.0, we've now added a side menu, as per Dashboard 1.0. Currently, this <em>just</em> provides a link to the Dashboard UI, but gives us a canvas on which to expand functionality in the future.</p>
<h3 id="improved-events-system" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#improved-events-system"></a> Improved Events System</h3>
<p>We've re-structured the hierarchy of the events system to make it more streamlined. Now, the <code>ui-base</code> manages comms via single channels dedicated to each event type, and the widget's ID is then used as a topic. Previously, we had a separate channel for each <code>action:id</code>.</p>
<p>If you're interested in learning more about our events architecture, you can read about it <a href="https://dashboard.flowfuse.com/contributing/guides/events.html">here</a> in the docs.</p>
<h3 id="documentation-updates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#documentation-updates"></a> Documentation Updates</h3>
<p>It's not glamorous, but it's important. We've made sure that all documentation and help text inside Node-RED is fully up to date for the Dashboard 2.0 nodes. We've also include rendered examples for all widgets in our <a href="https://dashboard.flowfuse.com/">online documentation</a> too.</p>
<p>We've also made sure that any legacy options that had been transferred over from Dashboard 1.0 that haven't been fully implemented yet are temporarily hidden. This means, any options you're seeing, <em>should</em> be working. If they're not - it's a bug.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#what's-next%3F"></a> What's Next?</h2>
<p>We have a lot of things to keep us busy, we are documenting them all in GitHub, and have made public our <a href="https://github.com/orgs/FlowFuse/projects/15/views/1">planning board</a>. You can see what we're working on, what's coming up next, and what we've got planned for the future.</p>
<p>As always, we're open to ideas, feedback & contributions. If you'd like to get involved, please check out our GitHub Repository <a href="https://github.com/FlowFuse/node-red-dashboard">here</a>.</p>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/dashboard-community-update/#join-our-team"></a> Join Our Team</h2>
<p>If you'd like to be paid to directly contribute to Dashboard 2.0, we are hiring for a 2-3 month position to do just that:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/Why the Automation Pyramid blocks digital transformation - The Role of Unified NamespaceA Critical Examination of the Automation Pyramid's Obstruction to Digital Transformation2023-08-09T00:00:00Z<p>A few years ago, I wrote an <a href="https://www.linkedin.com/pulse/iiot-circle-marian-raphael-demme/">article</a>, in German, detailing my understanding of how the Automation Pyramid, a widely adopted reference model for the IT landscape of manufacturing firms, is essentially hindering digital transformation. Now, as conversations around the Unified Namespace (UNS) and particular frameworks continue to evolve, I revisit my earlier notions, review the latest updates to reference frameworks, and update my article.</p>
<!--more-->
<h2 id="the-pyramid%E2%80%99s-dilemma" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#the-pyramid%E2%80%99s-dilemma"></a> The Pyramid’s Dilemma</h2>
<p>The Automation Pyramid is grounded in the standard <a href="https://www.isa.org/products/ansi-isa-95-00-01-2010-iec-62264-1-mod-enterprise">ISA-95</a>, which aligns with <a href="https://www.iso.org/standard/57308.html">IEC 62264</a> and <a href="https://www.beuth.de/en/standard/din-en-62264-1/207270059">DIN EN 62264</a>. It delineates the functional hierarchy within a manufacturing enterprise. Over 25 variations of the Automation Pyramid exist in academic literature, all of them fundamentally mapping to the same core concept, tracing back to the <a href="https://en.wikipedia.org/wiki/Computer-integrated_manufacturing">Computer-integrated manufacturing</a> (CIM)-Pyramid of the 1970s. Although ISA-95 does not explicitly refer to a pyramid, it introduces five functional hierarchical levels often visualized as a pyramid.</p>
<h4 id="isa-95---visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#isa-95---visualization"></a> ISA-95 - Visualization</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ISA95-sAb2sw61Qu-650.avif 650w, https://flowfuse.com/img/ISA95-sAb2sw61Qu-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ISA95-sAb2sw61Qu-650.webp 650w, https://flowfuse.com/img/ISA95-sAb2sw61Qu-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ISA95-sAb2sw61Qu-650.jpeg 650w, https://flowfuse.com/img/ISA95-sAb2sw61Qu-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="ISA95" loading="lazy" decoding="async" src="https://flowfuse.com/img/ISA95-sAb2sw61Qu-650.jpeg" width="1300" height="731" /></picture></p>
<h4 id="automation-pyramid---visualization" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#automation-pyramid---visualization"></a> Automation Pyramid - Visualization</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-650.avif 650w, https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-650.webp 650w, https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-650.jpeg 650w, https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Automation Pyramid" loading="lazy" decoding="async" src="https://flowfuse.com/img/Automation-Pyramid-752K9D7VCA-650.jpeg" width="1300" height="891" /></picture>
Source: Katti, Badarinath. (2020). Ontology-Based Approach to Decentralized Production Control in the Context of Cloud Manufacturing Execution Systems. 10.13140/RG.2.2.11486.46402.</p>
<p>A notable critique of ISA-95 is the absence of some operational functions and hierarchical levels commonly seen in manufacturing, leading to a rigidity that limits its applicability. This inflexibility has been acknowledged in a more recent framework, called the <a href="https://www.isa.org/intech-home/2019/march-april/features/rami-4-0-reference-architectural-model-for-industr">"Reference Architectural Model Industry 4.0"</a> RAMI 4.0 (<a href="https://www.beuth.de/en/norm/pd-iec-pas-63088/272832590">IEC PAS 63088</a>). As a result, the authors' introduced a <a href="https://syc-se.iec.ch/wp-content/uploads/2019/10/Reference_Architecture_final.pdf">"Smart Grid Architecture Model"</a> (SGAM) with three primary dimensions: Life Cycle & Value Stream (<a href="https://www.vde-verlag.de/iec-standards/248992/iec-62890-2020.html">IEC 62890</a>), Hierarchy Levels (<a href="https://www.iso.org/standard/57308.html">IEC 62264</a> and <a href="https://www.vde-verlag.de/iec-standards/216764/iec-61512-4-2009.html">IEC 61512</a>), and six main layers displaying the functional architecture of the asset and the separation into physical and digital world.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/RAMI40-pGTuEcO-TN-520.gif 520w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="RAMI4.0" loading="lazy" decoding="async" src="https://flowfuse.com/img/RAMI40-pGTuEcO-TN-520.webp" width="520" height="292" /></picture></p>
<p>However, my primary critique revolves around another issue – the structure and proposed communication methodology. Models based on layers, where each tier represents a functional area and could be covered by one or more applications, almost always lead to three fundamental problems:</p>
<h3 id="problem-1%3A-information-loss-and-transaction-costs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#problem-1%3A-information-loss-and-transaction-costs"></a> Problem 1: Information Loss and Transaction Costs</h3>
<p>In the traditional model, data collection flows upward from Levels 0 to 4, while planning goes downward from Level 4 to 0. Information traversing from Level 0 to Level 4 has to pass through at least four stages. Despite the theoretical lossless transmission of information, the practical scenario inevitably results in some degree of information loss between levels. The result is that the original information from Level 0 arrives at Level 4 late, altered, or not at all.</p>
<p><strong>Example:</strong> In a manufacturing plant, multiple sensors at Level 1 detect a sudden event. By the time this information passes through intermediary layers (e.g. PLC, SCADA, MES) to reach Level 4 where a planning decision can be made, it is delayed and distorted due to the multiple transitions. The factory might suffer damage before proper actions are taken because the original data didn't arrive on time or at all.</p>
<h3 id="problem-2%3A-the-expense-of-one-to-one-connections" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#problem-2%3A-the-expense-of-one-to-one-connections"></a> Problem 2: The Expense of One-to-One Connections</h3>
<p>The Automation Pyramid is based on different layers. Consequently, one-to-one connections between IT systems become a necessity for data transfer between levels. For example, Level 3 IT systems need at least two connections to the adjacent levels. This can lead to thousands of one-to-one interfaces between IT systems, incurring exorbitant costs for projects and maintenance.</p>
<p><strong>Example:</strong> In a semiconductor company, the Manufacturing Execution System (MES) serves as a critical intermediary in the Automation Pyramid. It must be integrated both with PLCs at the lower level for real-time control and with the ERP system at the higher level for business planning. This complex integration leads to the creation of numerous one-to-one connections. Furthermore, in implementing Industry 4.0 use cases like analytical applications, MES data is often required, creating even more connections. The multitude of connections complicates the system, making changes extremely difficult and maintenance intensive. This inflexibility becomes a barrier to adaptability and growth, hindering the efficient digital transformation of the manufacturing process.</p>
<h3 id="problem-3%3A-ai's-dependence-on-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#problem-3%3A-ai's-dependence-on-data"></a> Problem 3: AI's Dependence on Data</h3>
<p>Artificial intelligence (AI) requires extensive, well-organized data. Given the current architecture, data would have to be collected and prepared from case to case for each individual system and level. This would invariably lead to numerous new one-to-one connections, offering no flexibility. Hence, AI and the Automation Pyramid can only collaborate in a significantly restricted manner.</p>
<p><strong>Example:</strong> A car manufacturing firm aims to leverage a neural network for predictive maintenance. Within the constraints of the existing Automation Pyramid's architecture, the positioning for such an application is nonexistent. To train the neural network and subsequently analyse the data, a consolidation of varying hierarchical data is essential, such as sensory input, maintenance records, production scheduling plans, etc. Under the current architecture, the introduction of this application precipitates the creation of a multitude of new one-to-one connections. Consequently, it underscores the pressing need to rethink the structural paradigms.</p>
<h2 id="iiot-circle-and-unified-namespace" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#iiot-circle-and-unified-namespace"></a> IIoT Circle and Unified Namespace</h2>
<p>To overcome the limitations of traditional industrial data architecture, a paradigm shift towards a modern distributed architecture is necessary. Rather than allowing data to exist in silos within and across layers of the technology stack, data should be made accessible in a unified manner, creating a single, centralized repository. This approach facilitates a single centralized source for all enterprise systems to access the required data for their operations. This framework, which I have been calling the IIoT Circle, modernizes the original idea of the Automation Pyramid. A "Unified Namespace" operates as the core element that processes, and permits data streams to be loaded and exported from other systems. All other applications communicate exclusively through the Unified Namespace, requiring only a single interface to be maintained per application.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-650.avif 650w, https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-650.webp 650w, https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-650.jpeg 650w, https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="IToT Circle Image" loading="lazy" decoding="async" src="https://flowfuse.com/img/IIoT-Circle-DlMPBfxJt0-650.jpeg" width="1300" height="731" /></picture></p>
<p>In essence, Unified Namespace serves as the main data exchange hub within an organization. It structures, organizes, and maintains a real-time flow of data from a variety of sources, becoming the indisputable source of truth across the business. It simplifies data integration, eliminating the frequently convoluted, layered approach of traditional data systems.</p>
<h3 id="single-source-of-truth" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#single-source-of-truth"></a> Single Source of Truth</h3>
<p>The Unified Namespace breaks down the linear and deterministic data structure, which create data silos restricted to their specific systems. Instead, Unified Namespace centralizes data from across the entire organization. This results in a 'single source of truth' - a consolidated, current, and comprehensive overview of the organization's data.</p>
<h3 id="the-organizational-structure" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#the-organizational-structure"></a> The Organizational Structure</h3>
<p>Unified Namespace organizes data using a semantic hierarchy, similar to a meticulously arranged file share system. It can use the <a href="https://www.isa.org/products/ansi-isa-95-00-02-2018-enterprise-control-system-i">ISA-95 part 2</a> or the RAMI 4.0 Hierarchy Level standards to structure the hierarchy. This data organization facilitates navigation, management, and decision-making.</p>
<h3 id="the-pub-sub-approach" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#the-pub-sub-approach"></a> The Pub-Sub Approach</h3>
<p>The Publish-Subscribe (Pub-Sub) model facilitates communication that decouples the sender (publisher) from the receiver (subscriber), providing an efficient communication protocol to avoid one-to-one connections. It offers flexibility and scalability as it allows for one-to-many and many-to-one communications, enabling data to flow freely between systems.</p>
<h2 id="a-necessity-for-open-source" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#a-necessity-for-open-source"></a> A Necessity for Open Source</h2>
<p>Moreover, in this discourse on the Unified Namespace, we cannot overlook the role of open-source. Owning foundational digital services, such as the Unified Namespace, is a necessity for any corporation embarking on its digital transformation journey. This ownership provides a solid foundation, allowing companies to chart their destinies. To avoid the constraining bounds of vendor lock-in, which can significantly limit a company's digital capabilities; open-source or self-developed software offers the best recourse. By its nature, open-source promotes transparency, collaboration, and freedom of use. These aspects are fundamental to fostering innovation and continuous improvement. As exemplified by the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/ming-blog/">MING Stack</a>, open source software can and should be incorporated into every level of the hierarchy.</p>
<h2 id="summary-%E2%80%93-advancing-current-standards" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/isa-95-automation-pyramid-to-unified-namespace/#summary-%E2%80%93-advancing-current-standards"></a> Summary – Advancing Current Standards</h2>
<p>The lag of standards behind the latest innovation is an open secret, a problem rooted in the nature and development of these standards. However, maintaining and updating these standards remains crucial as many people adhere to them.</p>
<p>ISA-95 Part 6 mentions a Messaging Service Model (MSM) and proposes a "publish-subscribe" model as an option for transactions. This is a great step in the right direction. My recommendation for ISA-95 is to further develop Part 6 to clearly delineate the implementation pattern of the Unified Namespace. Additionally, ISA-95 Part 1 should make explicit references to the communication pattern detailed in Part 6 and transition from a layer model to a cycle, with the Unified Namespace as an integral part of the framework.</p>
<p>RAMI 4.0's Communication Layer is rather abstract. It suggests the use of OPC-UA for everything in manufacturing, from "Product" to "Work Center". For "Enterprise" and "Connected World", it states "still undecided". My improvement suggestion is to define the "Communication Layer" new and to be more technology-agnostic. Be more explicit about what needs to be done and more flexible about how to do it.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/Community News August 2023Your monthly update for the FlowFuse and Node-RED communities2023-08-08T00:00:00Z<p>Welcome to the FlowFuse newsletter for August 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="new-release" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#new-release"></a> New Release</h2>
<p>Last week we released FlowFuse 1.10, featuring improvements to our device management solutions and the new ability to import environment variable templates. Read about the details of FlowFuse 1.10 in our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/">release announcement</a>.</p>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#upcoming-events"></a> Upcoming events</h2>
<h3 id="getting-started-with-opc-ua-and-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#getting-started-with-opc-ua-and-node-red"></a> Getting Started with OPC-UA and Node-RED</h3>
<p>OPC-UA is a popular communication protocol used to communicate industrial data between different types of hardware and software. Our next webinar show how to use Node-RED to create an OPC-UA client that can read OPC data and visualize the data in Node-RED. We are glad to welcome Mika Karaila, Research Director @ Valmet Automation and creator of the OPC-UA nodes, as our webinar speaker.</p>
<p><a href="https://flowfuse.com/webinars/2023/getting-started-opcua-node-red/">Sign-up today</a> to join us on July 27.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#from-our-blog"></a> From our Blog</h2>
<ul>
<li>
<p>Our developer advocate, Richard Meyer, published a series of articles on OPC-UA:</p>
<ul>
<li>Part 1: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/">How to Deploy a Basic OPC-UA Server in Node-RED</a></li>
<li>Part 2: <a href="https://flowfuse.com/node-red/protocol/opc-ua/">How to Build a Secure OPC-UA Server for PLCs in Node-RED</a></li>
<li>Part 3: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/">How to Build an OPC UA Client Dashboard in Node-RED</a></li>
</ul>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/">First Pre-Alpha Release of the new Node-RED Dashboard</a> - update on FlowFuse's work to develop the next generation of Node-RED dashboard.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/">How to add images to Node-RED dashboards when using FlowFuse</a> - some tips on how to add images to a dashboard when running Node-RED instances in a docker environment, like FlowFuse.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/">Creating a Historical Data Dashboard with InfluxDB and Node-RED</a> - an in-depth article on how to store historical data in InfluxDB that can be visualize with Node-RED dashboard.</p>
</li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#from-the-community"></a> From the Community</h2>
<ul>
<li><strong>Featured Node</strong>: <a href="https://flows.nodered.org/node/node-red-contrib-buffer-parser">Buffer Parser</a> - a really useful node for parsing buffers/arrays that are common in industrial data.</li>
</ul>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/community-news-08/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/FlowFuse 1.10 Release Now AvailableNew FlowFuse 1.10 also includes improvements to device management and importing environment variable templates.2023-08-03T00:00:00Z<p>FlowFuse 1.10 release includes improvements to device management and importing environment variable templates.</p>
<!--more-->
<h2 id="import-environment-variable-templates-%232372" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#import-environment-variable-templates-%232372"></a> Import Environment Variable Templates <a href="https://github.com/FlowFuse/flowfuse/issues/2372">#2372</a></h2>
<p>FlowFuse 1.10 now allows users to import environment variable templates. This makes it much easier and less error prone to maintain and add new environment variables to Node-RED instances running on FlowFuse.</p>
<h2 id="devops-pipelines-now-can-include-devices-%232243" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#devops-pipelines-now-can-include-devices-%232243"></a> DevOps Pipelines now can include devices <a href="https://github.com/FlowFuse/flowfuse/issues/2243">#2243</a></h2>
<p>DevOps pipelines have proven very popular for creating dev/test/production environments for Node-RED flow development. Now, devices can be associated with a pipeline so when a snapshot is created it can be pushed to all the devices associated with the pipeline. This will improve the overall quality and reliability of Node-RED development for remote devices.</p>
<h2 id="devices-can-now-access-the-team-library-%232294" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#devices-can-now-access-the-team-library-%232294"></a> Devices can now access the team library <a href="https://github.com/FlowFuse/flowfuse/issues/2294">#2294</a></h2>
<p>Team libraries allow Node-RED development team to share common flows and nodes through a shared library. Until the 1.10 release, Node-RED running remotely on a device did not have access to the team library. This limitation is now removed so device development can benefit from reusing standard flows.</p>
<h2 id="other-new-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#other-new-features"></a> Other New Features</h2>
<ul>
<li>Add description of device type field <a href="https://github.com/FlowFuse/flowfuse/issues/2428">#2428</a></li>
<li>Improve reliability of device editor <a href="https://github.com/FlowFuse/flowfuse/issues/2483">#2483</a></li>
<li>Improve error feedback from device editor tunnel <a href="https://github.com/FlowFuse/flowfuse/issues/2473">#2473</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#bug-fixes"></a> Bug Fixes</h2>
<ul>
<li>Improve visualization of Last Seen & Last Known with large amounts of devices. <a href="https://github.com/FlowFuse/flowfuse/issues/2380">#2380</a></li>
<li>Fix billing information error in FlowFuse Cloud <a href="https://github.com/FlowFuse/flowfuse/issues/2416">#2416</a></li>
<li>Fix T&C checkbox on sign-up page <a href="https://github.com/FlowFuse/flowfuse/issues/2419">#2419</a></li>
</ul>
<h2 id="community-contributions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#community-contributions"></a> Community Contributions</h2>
<p>Thanks to our community members for their contributions to this release.</p>
<ul>
<li><a href="https://github.com/dfulgham">dfulgham</a> - Added support for annotation substitutions <a href="https://github.com/FlowFuse/flowforge-driver-k8s/pull/95">#95</a></li>
<li><a href="https://github.com/elenaviter">elanaviter</a> - Editors: allow optional service account linkage <a href="https://github.com/FlowFuse/flowforge-driver-k8s/pull/92">#92</a></li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.10.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/08/flowforge-1-10-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go the the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/How to Build an OPC UA Client Dashboard in Node-RED - Part 3Interactive OPC UA Client dashboard that communicates with a 3rd party OPC UA Server2023-07-27T00:00:00Z<p>This article is the third and final part of our OPC UA content series. In the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/">first article</a>, we cover some OPC UA fundamentals and walk through an example OPC UA Server flow. In the <a href="https://flowfuse.com/node-red/protocol/opc-ua/">second article</a>, we built a SSL-secured OPC UA server using data from an Allen Bradley PLC as a source.
In 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.</p>
<!--more-->
<p>This article will requires the <a href="https://prosysopc.com/products/opc-ua-simulation-server/">Prosys OPC UA Simulation Server</a>, 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.</p>
<p>Note: full source code for the OPC Client Dashboard is included at the end of the article.</p>
<h2 id="custom-nodes-used-%26-assumptions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#custom-nodes-used-%26-assumptions"></a> Custom Nodes Used & Assumptions</h2>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/">earlier article</a> where the process on installing custom nodes is explained in detail.</p>
<ul>
<li><a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">@flowfuse/node-red-dashboard</a></li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-opcua">node-red-contrib-opcua</a></li>
<li><a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard-2-ui-led">@flowfuse/node-red-dashboard-2-ui-led</a></li>
</ul>
<p>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.</p>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/dashboard/">Node-RED Dashboard 2.0 guides</a>.</p>
<h2 id="install-and-deploy-the-prosys-opc-ua-simulation-server" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#install-and-deploy-the-prosys-opc-ua-simulation-server"></a> Install and Deploy the Prosys OPC UA Simulation Server</h2>
<p>The Prosys OPC UA Simulation Server is <a href="https://prosysopc.com/products/opc-ua-simulation-server/evaluate/">free to download</a>, 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 <code>options -> switch to expert mode</code>.</p>
<p>This will give us access to the address space tab, which we will need to develop our client application in Node-RED.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/expert-mode-XONwVsGs5B-553.avif 553w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/expert-mode-XONwVsGs5B-553.webp 553w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="expert-mode.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/expert-mode-XONwVsGs5B-553.jpeg" width="553" height="292" /></picture></p>
<p>When the application is run, an endpoint url will be displayed on the <code>status</code> tab, along with an indication that the server is currently running.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-endpoint-url-1dSkqDR2vN-1491.avif 1491w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-endpoint-url-1dSkqDR2vN-1491.webp 1491w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opc-endpoint-url.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-endpoint-url-1dSkqDR2vN-1491.jpeg" width="1491" height="915" /></picture>
Copy the connection endpoint, but be warned that you will likely need to replace the computer name (in my case <code>DESKTOP-0K0483A</code>, with the actual IP address of the machine running the server. The IP address of the machine on my local network is <code>192.168.0.141</code>, which changes my UA TCP endpoint address to <code>opc.tcp://192.168.0.141:53530/OPCUA/SimulationServer</code>.</p>
<p>Now the simulation server is set up and we are ready to start developing the OPC Client application.</p>
<h2 id="objectives-of-the-node-red-opc-client-dashboard-application" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#objectives-of-the-node-red-opc-client-dashboard-application"></a> Objectives of the Node-RED OPC Client Dashboard Application</h2>
<p>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:</p>
<ol>
<li>Browse hierarchical server address space structure & display on a dashboard</li>
<li>Read OPC UA values from various namespaces, showing a variety of datatypes and different ways they can be visualized</li>
<li>Write OPC UA values back to the OPC UA server directly from the OPC UA Client dashboard</li>
<li>Read alarms & events from the OPC UA Server and display them on the dashboard</li>
</ol>
<p>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.</p>
<h2 id="browse-hierarchical-server-address-space-structure-with-opc-ua-browser-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#browse-hierarchical-server-address-space-structure-with-opc-ua-browser-node"></a> Browse Hierarchical Server Address Space Structure With OPC UA Browser Node</h2>
<p>The first flow will browse the hierarchical OPC UA Server address space structure and display it on the dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/image-20230727-085611-yJHf809Wdv-917.avif 917w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/image-20230727-085611-yJHf809Wdv-917.webp 917w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="image-20230727-085611.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/image-20230727-085611-yJHf809Wdv-917.jpeg" width="917" height="346" /></picture></p>
<p>You can import this flow into Node-RED using the code below:</p>
<div id="nr-flow-124" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow124 = "\n[{\"id\":\"ca62be3e01388319\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Browse Hierarchical Address Space Structure & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":[\"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\":[],\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[{\"t\":\"set\",\"p\":\"Objects.Simulation.nodeId\",\"pt\":\"flow\",\"to\":\"payload[2].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[2].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\":[]},{\"id\":\"d0c969b6a59fac3a\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyObjects Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.nodeId\",\"pt\":\"flow\",\"to\":\"payload[4].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":340,\"wires\":[[\"52dd2e5dcddad58f\"]]},{\"id\":\"639da01fc957e547\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticData Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.nodeId\",\"pt\":\"flow\",\"to\":\"payload[3].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[3].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.AnalogItemArrays.nodeId\",\"pt\":\"flow\",\"to\":\"payload[1].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[1].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":850,\"y\":460,\"wires\":[[\"157322c9c360446d\"]]},{\"id\":\"e7c55f412ef86543\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticArrayVariables Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.StaticArrayVariables.nodeId\",\"pt\":\"flow\",\"to\":\"payload[6].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[6].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":860,\"y\":400,\"wires\":[[\"a5acdccfd2033aec\"]]},{\"id\":\"de21b7ad98a05833\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyDevice Object\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.MyDevice.nodeId\",\"pt\":\"flow\",\"to\":\"payload[0].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[{\"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\":[],\"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\":[]},{\"id\":\"1c49fa5142d2cf17\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Global Address Space Folder Browse\",\"info\":\"\",\"x\":410,\"y\":220,\"wires\":[]},{\"id\":\"335878527020598c\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"Simulation\",\"order\":1,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 3</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 5</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder-arrow-down\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <p>Namespace 6</p>\\n <div class=\\\"inline-content\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1100,\"y\":520,\"wires\":[[]]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer\",\"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\":\"/opcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow124.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-124') })</script>
<p>To understand what is going on in this flow, we must refer back to the OPC UA Simulation Server <code>Address Space</code> tab.</p>
<p>When we browse the OPC Server base folder structure in Node-RED, we will be browsing everything included under the <code>Objects</code> tree.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/address-space-GTnyuyqBAk-617.avif 617w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/address-space-GTnyuyqBAk-617.webp 617w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="address-space.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/address-space-GTnyuyqBAk-617.jpeg" width="617" height="574" /></picture>
In 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 <code>Topic</code> blank. By doing this, we will browse the entire folder structure by default.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/image-20230727-090252-Mo57laBVp4-1375.avif 1375w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/image-20230727-090252-Mo57laBVp4-1375.webp 1375w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="image-20230727-090252.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/image-20230727-090252-Mo57laBVp4-1375.jpeg" width="1375" height="355" /></picture>
The configuration of the endpoint properties includes no security credentials, as shown below.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/endpoint-configure-ahbCzBrGLI-461.avif 461w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/endpoint-configure-ahbCzBrGLI-461.webp 461w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="endpoint-configure.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/endpoint-configure-ahbCzBrGLI-461.jpeg" width="461" height="369" /></picture></p>
<p>Using the output of a debug node, we get from the OPC UA Browser yield a payload with an array of 5 objects.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/address-debug-5FhYgmtk6--1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/address-debug-5FhYgmtk6--1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="address-debug.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/address-debug-5FhYgmtk6--1920.jpeg" width="1920" height="298" /></picture></p>
<p>Each object returned represents the 5 objects that are in our OPC UA Server Objects tree.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/browse-payload-1-SF32TKjJ39-765.avif 765w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/browse-payload-1-SF32TKjJ39-765.webp 765w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="browse-payload-1.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/browse-payload-1-SF32TKjJ39-765.jpeg" width="765" height="266" /></picture></p>
<p>However, of those 5 objects, only 3 of them are folders that contain actual OPC values. <code>MyObjects</code>, <code>Simulation</code>, and <code>StaticData</code>. We can ignore <code>Aliases</code> and <code>Server</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/address-space-folders-only-wHXmvFf8t5-389.avif 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/address-space-folders-only-wHXmvFf8t5-389.webp 389w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="address-space-folders-only.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/address-space-folders-only-wHXmvFf8t5-389.jpeg" width="389" height="441" /></picture></p>
<p>So looking deeper into the payload of our global browse from the <code>OPC UA Browser node</code>, we can drill down into the details and see how they correlate with the folders in the server.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/browse-node-if_5jPubJi-1497.avif 1497w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/browse-node-if_5jPubJi-1497.webp 1497w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="browse-node.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/browse-node-if_5jPubJi-1497.jpeg" width="1497" height="1376" /></picture></p>
<p>As shown above, element 2 in the array returned from the global browse corresponds to the <code>Simulation</code> folder. And we are interested in two important values in this data-structure - the <code>NodeId</code>, which is topic an OPC Client uses to point specific OPC values, and the <code>browseName</code>, 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/simulation-folder-K6kzOji5J5-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/simulation-folder-K6kzOji5J5-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="simulation-folder.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/simulation-folder-K6kzOji5J5-1920.jpeg" width="1920" height="809" /></picture>
This change node is grabbing the <code>nodeId</code> and <code>browseName</code> . The <code>nodeId</code> is stored in a context variable for later use, while the <code>browseName</code> is used as the payload to be displayed on our dashboard.</p>
<p>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</p>
<ul>
<li>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.</li>
</ul>
<p>If you deploy the flow and pull up the dashboard, it results in the following output -</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/address-space-dashboard-937mQuTjHk-778.avif 778w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/address-space-dashboard-937mQuTjHk-778.webp 778w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="address-space-dashboard.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/address-space-dashboard-937mQuTjHk-778.jpeg" width="778" height="737" /></picture>
Showing 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 <code>OPC UA Browser</code> node. The browser node is best used for reading OPC UA values, which will be covered next.</p>
<h2 id="read-opc-ua-values-using-opc-ua-browser-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#read-opc-ua-values-using-opc-ua-browser-node"></a> Read OPC UA Values Using OPC UA Browser Node</h2>
<p>The next set of flows read OPC UA values from the server and displays them on the dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/read-opc-values-7yBFEjZUws-723.avif 723w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/read-opc-values-7yBFEjZUws-723.webp 723w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="read-opc-values.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/read-opc-values-7yBFEjZUws-723.jpeg" width="723" height="521" /></picture>
You can import these flows into Node-RED using the code below:</p>
<div id="nr-flow-125" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow125 = "\n[{\"id\":\"ca62be3e01388319\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Browse Hierarchical Address Space Structure & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":[\"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\":[],\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[{\"t\":\"set\",\"p\":\"Objects.Simulation.nodeId\",\"pt\":\"flow\",\"to\":\"payload[2].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[2].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\":[]},{\"id\":\"d0c969b6a59fac3a\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyObjects Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.nodeId\",\"pt\":\"flow\",\"to\":\"payload[4].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":340,\"wires\":[[\"52dd2e5dcddad58f\"]]},{\"id\":\"639da01fc957e547\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticData Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.nodeId\",\"pt\":\"flow\",\"to\":\"payload[3].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[3].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.AnalogItemArrays.nodeId\",\"pt\":\"flow\",\"to\":\"payload[1].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[1].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":850,\"y\":460,\"wires\":[[\"157322c9c360446d\"]]},{\"id\":\"e7c55f412ef86543\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticArrayVariables Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.StaticArrayVariables.nodeId\",\"pt\":\"flow\",\"to\":\"payload[6].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[6].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":860,\"y\":400,\"wires\":[[\"a5acdccfd2033aec\"]]},{\"id\":\"de21b7ad98a05833\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyDevice Object\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.MyDevice.nodeId\",\"pt\":\"flow\",\"to\":\"payload[0].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[{\"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\":[],\"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\":[]},{\"id\":\"1c49fa5142d2cf17\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Global Address Space Folder Browse\",\"info\":\"\",\"x\":410,\"y\":220,\"wires\":[]},{\"id\":\"335878527020598c\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"Simulation\",\"order\":1,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 3</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 5</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder-arrow-down\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <p>Namespace 6</p>\\n <div class=\\\"inline-content\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1100,\"y\":520,\"wires\":[[]]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer\",\"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\":\"/opcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\":[\"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\":[],\"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\":[{\"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\":[]},{\"id\":\"13f5c98b7fd5f5da\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Counter Value\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[1].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\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[2].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[3].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.value\",\"tot\":\"msg\"}],\"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\":[]},{\"id\":\"82aa12173dd7bbca\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Parse Simulation Values\",\"info\":\"\",\"x\":1070,\"y\":640,\"wires\":[]},{\"id\":\"57d8777e34b55b7b\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":640,\"wires\":[]},{\"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\":[{\"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\":[]},{\"id\":\"c4d4a3b0df372e4c\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"group\":\"af263064820fb7d0\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"Random Number\",\"label\":\"Random Number\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1350,\"y\":740,\"wires\":[]},{\"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\":[\"#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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[\"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\":[],\"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\":[{\"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\":[]},{\"id\":\"18d21607c87ab153\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Get ByteAnalogItemArray Value\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$string(payload[0].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\":[{\"t\":\"nempty\"}],\"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\":[],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"nempty\"}],\"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\":[]},{\"id\":\"c74606c48ccf5a40\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Parse StaticData Values\",\"info\":\"\",\"x\":1070,\"y\":980,\"wires\":[]},{\"id\":\"053bda13f2a2eabe\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":980,\"wires\":[]},{\"id\":\"277dcf430dc86996\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"ByteAnalogItemArray\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1340,\"y\":1020,\"wires\":[]},{\"id\":\"d708e6264cec0070\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"BooleanArray\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1320,\"y\":1100,\"wires\":[]},{\"id\":\"3d4f386e812e8b5f\",\"type\":\"ui-group\",\"name\":\"StaticData Values\",\"page\":\"44d3feb2a1143d7b\",\"width\":\"4\",\"height\":\"1\",\"order\":3,\"showTitle\":true,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow125.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-125') })</script>
<p>The values are derived from the <code>nodeId</code> values we stored in memory in our previous flow, via our <code>change</code> nodes in the previous flow.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-context-nodeid-6rC5Y9yn2t-725.avif 725w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-context-nodeid-6rC5Y9yn2t-725.webp 725w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="flow-context-nodeid.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-context-nodeid-6rC5Y9yn2t-725.jpeg" width="725" height="1030" /></picture>
As stated earlier, you reference a OPC UA topic by its <code>nodeId</code>. So we will use these node IDs to read actual values from our OPC nodes.</p>
<p>In our first flow, we want to read the values in the <code>Simulation</code> folder at a 1 second interval. So we use an <code>inject</code> node with a <code>msg.topic</code> that references the <code>nodeId</code> corresponding to the <code>Simulation</code> folder.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/simulation-injection-Q-sJSo3Ig8-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/simulation-injection-Q-sJSo3Ig8-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="simulation-injection.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/simulation-injection-Q-sJSo3Ig8-1920.jpeg" width="1920" height="1193" /></picture>
That <code>msg.topic</code> tells the <code>OPC UA Browser</code> node what <code>nodeId</code> to browse. If we look at the debug output of the browser <code>msg.payload</code>, we can see that it produces an array of 7 objects, and an empty set array.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/simulation-debug-jWOAJlLbRY-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/simulation-debug-jWOAJlLbRY-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="simulation-debug.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/simulation-debug-jWOAJlLbRY-1920.jpeg" width="1920" height="463" /></picture>
If 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 <code>switch</code> node to filter out the empty set.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/empty-check-PxeQZ-0nXA-1411.avif 1411w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/empty-check-PxeQZ-0nXA-1411.webp 1411w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="empty-check.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/empty-check-PxeQZ-0nXA-1411.jpeg" width="1411" height="1299" /></picture>
Now only non-empty payloads will be passed, preventing the values being reset to 0 on each read.</p>
<p>Now we can actually read the values. To do this, we use a <code>change</code> node again, referencing the non-empty payload and drilling down to the <code>value</code> that corresponds to the <code>name</code> of the node we want to read. In this case, we’re getting the value of the node <code>Counter</code> located in the <code>Simulation</code> folder.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/get-counter-value-_U-Vv5c-Bb-1920.avif 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/get-counter-value-_U-Vv5c-Bb-1920.webp 1920w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="get-counter-value.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/get-counter-value-_U-Vv5c-Bb-1920.jpeg" width="1920" height="868" /></picture>
Going back to our OPC Server, we can see that exactly where that value is derived below -</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/sim-counter-server-ibKYm0jerQ-1217.avif 1217w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/sim-counter-server-ibKYm0jerQ-1217.webp 1217w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="sim-counter-server.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/sim-counter-server-ibKYm0jerQ-1217.jpeg" width="1217" height="843" /></picture></p>
<p>Now we add a <code>gauge</code> 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/counter-propertie-Hr5KQpmJOI-519.avif 519w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/counter-propertie-Hr5KQpmJOI-519.webp 519w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="counter-properties.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/counter-propertie-Hr5KQpmJOI-519.jpeg" width="519" height="767" /></picture>
Now 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.</p>
<p>The end result on the dashboard now looks like this -</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/opc-read-dashboard-uyzOaK1AhY-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opc-read-dashboard.gif" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-read-dashboard-uyzOaK1AhY-800.webp" width="800" height="450" /></picture></p>
<h2 id="write-opc-ua-values-to-server-using-opcua-item-and-opc-ua-client-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#write-opc-ua-values-to-server-using-opcua-item-and-opc-ua-client-nodes"></a> Write OPC UA Values To Server Using OpcUa-Item and Opc-Ua-Client Nodes</h2>
<p>The next flow writes OPC UA values to the server using dashboard UI elements.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/write-mydevice-eIU3YiDe1z-1401.avif 1401w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/write-mydevice-eIU3YiDe1z-1401.webp 1401w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="write-mydevice.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/write-mydevice-eIU3YiDe1z-1401.jpeg" width="1401" height="415" /></picture>
You can import this flow into Node-RED using the code below:</p>
<div id="nr-flow-126" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow126 = " \n[{\"id\":\"3de6c861611c3afa\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Write Mydevices values to OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":[\"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\":[[],[]]},{\"id\":\"9fa33d1c9c621611\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Dashboard Input\",\"info\":\"\",\"x\":200,\"y\":1480,\"wires\":[]},{\"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\":[[],[]]},{\"id\":\"77bcb828bec95336\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Call OPC UA Item\",\"info\":\"\",\"x\":470,\"y\":1480,\"wires\":[]},{\"id\":\"afa83dbb46449d4a\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Write OPC UA Item to Client\",\"info\":\"\",\"x\":740,\"y\":1480,\"wires\":[]},{\"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://192.168.56.1:53530/OPCUA/SimulationServer\",\"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\":\"/opcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow126.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-126') })</script>
<p>We have two values to write, a boolean value corresponding to the node object <code>MySwitch</code>, and an integer value corresponding to the object <code>MyLevel</code>. Therefore, we will use a toggle switch to toggle the <code>MySwitch</code>, and a slider to modify <code>MyLevel</code>.</p>
<p>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%.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/level-range-exaL1QwQLR-1900.avif 1900w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/level-range-exaL1QwQLR-1900.webp 1900w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="level-range.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/level-range-exaL1QwQLR-1900.jpeg" width="1900" height="716" /></picture>
For the <code>OpcUa-Item</code> nodes, copy the <code>NodeId</code> corresponding to each device,</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/copy-node-id-gvbKOWOD1W-1502.avif 1502w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/copy-node-id-gvbKOWOD1W-1502.webp 1502w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="copy-node-id.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/copy-node-id-gvbKOWOD1W-1502.jpeg" width="1502" height="375" /></picture>
and paste it into <code>OpcUa-Item</code> node. You must also ensure the data-type matches with the value you’re writing to.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opcua-item-NMw9sxkqdz-1271.avif 1271w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opcua-item-NMw9sxkqdz-1271.webp 1271w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opcua-item.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/opcua-item-NMw9sxkqdz-1271.jpeg" width="1271" height="856" /></picture>
The <code>Opc-Ua-Client</code> needs to have an endpoint and the action changed to <code>WRITE</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/client-node-Mcbyiq-uwV-1394.avif 1394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/client-node-Mcbyiq-uwV-1394.webp 1394w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="client-node.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/client-node-Mcbyiq-uwV-1394.jpeg" width="1394" height="855" /></picture>
The process is the same for <code>MySwitch</code> and <code>MyLevel</code>, the only difference being what <code>NodeId</code> is referenced in the <code>OpcUa-Item</code> node.</p>
<p>When deployed, you can confirm values are being written to from the client to the server from the dashboard.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/opc-write-GucS5Kg_dE-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opc-write.gif" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-write-GucS5Kg_dE-800.webp" width="800" height="390" /></picture></p>
<h2 id="read-alarms-%26-events-from-opc-ua-server-using-opcua-event-and-opc-ua-client-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#read-alarms-%26-events-from-opc-ua-server-using-opcua-event-and-opc-ua-client-nodes"></a> Read Alarms & Events from OPC UA Server Using OpcUa-Event and Opc-Ua-Client Nodes</h2>
<p>Our last flow we’ll show how to read OPC UA Alarms & Events.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-event-flow-5P3xoE6IaG-1054.avif 1054w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-event-flow-5P3xoE6IaG-1054.webp 1054w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opc-event-flow.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-event-flow-5P3xoE6IaG-1054.jpeg" width="1054" height="265" /></picture>
You can import this flow into Node-RED using the code below:</p>
<div id="nr-flow-127" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow127 = "\n[{\"id\":\"a6e9abacd0bdf3b6\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Read Alarms & Events From OPC UA Server\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":[\"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\":[]},{\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\"],[]]},{\"id\":\"c7e8919b636cb51d\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Event Text\",\"rules\":[{\"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\":[{\"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\":[{\"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\":[]},{\"id\":\"5b4d1bd8b342fc05\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Get OPC Events from Client\",\"info\":\"\",\"x\":720,\"y\":1820,\"wires\":[]},{\"id\":\"ba1ea89438335cb8\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Display Events on Dashboard\",\"info\":\"\",\"x\":1240,\"y\":1720,\"wires\":[]},{\"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\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1240,\"y\":1840,\"wires\":[]},{\"id\":\"0b8ac86e5e4f9f8d\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Timestamp\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1280,\"y\":1880,\"wires\":[]},{\"id\":\"62b2e14ce0429eef\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Severity\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1270,\"y\":1920,\"wires\":[]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer\",\"secpol\":\"None\",\"secmode\":\"None\",\"none\":true,\"login\":false,\"usercert\":false,\"usercertificate\":\"\",\"userprivatekey\":\"\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\":\"/opcua\",\"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\"}}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow127.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-127') })</script>
<p>We use an inject node to trigger the <code>OpcUa-Event</code> node. In the properties of the event node, we get the <code>NodeId</code> from the <code>MyLevelAlarm</code> event from the OPC Server -</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/mylevel-event-DAdiz8agx3-1405.avif 1405w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/mylevel-event-DAdiz8agx3-1405.webp 1405w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="mylevel-event.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/mylevel-event-DAdiz8agx3-1405.jpeg" width="1405" height="559" /></picture>
And copy that <code>NodeId</code> into the <code>OpcUa-Event</code> node. Event type will be <code>BaseEvent (all)</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/event-node-properties-wyPltii_R4-1758.avif 1758w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/event-node-properties-wyPltii_R4-1758.webp 1758w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="event-node-properties.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/event-node-properties-wyPltii_R4-1758.jpeg" width="1758" height="1187" /></picture>
In the <code>Opc-Ua-Client</code> node, we set the <code>Action</code> to <code>EVENTS</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/client-events-4jRajoWxwS-1387.avif 1387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/client-events-4jRajoWxwS-1387.webp 1387w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="client-events.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/client-events-4jRajoWxwS-1387.jpeg" width="1387" height="1316" /></picture>
If we stick a debug node on the output of the client event, we can see how the OPC Server annunciates events.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/event-debug-tlQeoRlzL5-1826.avif 1826w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><source type="image/webp" srcset="https://flowfuse.com/img/event-debug-tlQeoRlzL5-1826.webp 1826w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="event-debug.png" loading="lazy" decoding="async" src="https://flowfuse.com/img/event-debug-tlQeoRlzL5-1826.jpeg" width="1826" height="838" /></picture>
Every time <code>MyLevel</code> exceeds certain thresholds (10%, 30%, 70% and 90%) it will flag a <code>Level Exceeded</code> alarm. The event is timestamped and assigned a severity level, which we will record and put onto the dashboard.</p>
<p>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.</p>
<p>Adding alarms and events to our dashboard creates the following result -</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/opc-event-yCV_SshiRg-800.gif 800w" sizes="(min-device-pixel-ratio: 1.25) 3840px, (min-resolution: 120dpi) 3840px, 1920px" /><img data-zoomable="" alt="opc-event.gif" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-event-yCV_SshiRg-800.webp" width="800" height="450" /></picture></p>
<h2 id="using-flowfuse-to-enhance-your-node-red-application%3A-security%2C-scalability%2C-and-robustness" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#using-flowfuse-to-enhance-your-node-red-application%3A-security%2C-scalability%2C-and-robustness"></a> Using FlowFuse to Enhance Your Node-RED Application: Security, Scalability, and Robustness</h2>
<p>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?</p>
<p>Enter <a href="https://flowfuse.com/">FlowFuse</a>, 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.</p>
<p>If you're interested in learning how to use Node-RED for professional use cases, check out our eBook: <a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">Ultimate Beginner's Guide to Professionals</a>. For additional resources, visit our <a href="https://flowfuse.com/node-red/core-nodes/">Node-RED Learning Resources section</a>, where you can explore integrations with different protocols, messaging services, databases, hardware, and much more.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-build-a-opc-client-dashboard-in-node-red/#conclusion"></a> Conclusion</h2>
<p>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.</p>
<p>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.</p>
<p>full source code for this project -</p>
<div id="nr-flow-128" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow128 = " \n[{\"id\":\"ca62be3e01388319\",\"type\":\"group\",\"z\":\"5b972161c4e0464e\",\"name\":\"Browse Hierarchical Address Space Structure & Display on Dashboard\",\"style\":{\"label\":true,\"color\":\"#000000\"},\"nodes\":[\"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\":[],\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\":[{\"t\":\"set\",\"p\":\"Objects.Simulation.nodeId\",\"pt\":\"flow\",\"to\":\"payload[2].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[2].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\":[]},{\"id\":\"d0c969b6a59fac3a\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyObjects Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.nodeId\",\"pt\":\"flow\",\"to\":\"payload[4].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":830,\"y\":340,\"wires\":[[\"52dd2e5dcddad58f\"]]},{\"id\":\"639da01fc957e547\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticData Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.nodeId\",\"pt\":\"flow\",\"to\":\"payload[3].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[3].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.AnalogItemArrays.nodeId\",\"pt\":\"flow\",\"to\":\"payload[1].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[1].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":850,\"y\":460,\"wires\":[[\"157322c9c360446d\"]]},{\"id\":\"e7c55f412ef86543\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"StaticArrayVariables Folder\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.StaticData.StaticArrayVariables.nodeId\",\"pt\":\"flow\",\"to\":\"payload[6].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[6].item.browseName.name\",\"tot\":\"msg\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":860,\"y\":400,\"wires\":[[\"a5acdccfd2033aec\"]]},{\"id\":\"de21b7ad98a05833\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"MyDevice Object\",\"rules\":[{\"t\":\"set\",\"p\":\"Objects.MyObjects.MyDevice.nodeId\",\"pt\":\"flow\",\"to\":\"payload[0].item.nodeId\",\"tot\":\"msg\"},{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.browseName.name\",\"tot\":\"msg\"}],\"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\":[{\"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\":[],\"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\":[]},{\"id\":\"1c49fa5142d2cf17\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"name\":\"Global Address Space Folder Browse\",\"info\":\"\",\"x\":410,\"y\":220,\"wires\":[]},{\"id\":\"335878527020598c\",\"type\":\"ui-template\",\"z\":\"5b972161c4e0464e\",\"g\":\"ca62be3e01388319\",\"group\":\"ef9998baf5f61e8a\",\"page\":\"\",\"ui\":\"\",\"name\":\"Simulation\",\"order\":1,\"width\":0,\"height\":0,\"head\":\"\",\"format\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 3</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"inline-content\\\">\\n <p>Namespace 5</p>\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder-arrow-down\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <p>Namespace 6</p>\\n <div class=\\\"inline-content\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"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\":\"<template>\\n <div class=\\\"d-flex align-center ml-3\\\">\\n <v-icon color=\\\"black\\\" icon=\\\"mdi-folder\\\" size=\\\"large\\\"></v-icon> \\n </div>\\n</template>\",\"storeOutMessages\":true,\"passthru\":true,\"resendOnRefresh\":true,\"templateScope\":\"local\",\"className\":\"\",\"x\":1100,\"y\":520,\"wires\":[[]]},{\"id\":\"53f4394dbf12c6b7\",\"type\":\"OpcUa-Endpoint\",\"endpoint\":\"opc.tcp://192.168.56.1:53530/OPCUA/SimulationServer\",\"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\":\"/opcua\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"61eee6fc60281b9b\",\"order\":1,\"className\":\"\",\"visible\":\"true\",\"disabled\":\"false\"},{\"id\":\"5355e0c476f9da3b\",\"type\":\"ui-base\",\"name\":\"My Dashboard\",\"path\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-notification\",\"ui-control\"],\"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\":[\"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\":[],\"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\":[{\"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\":[]},{\"id\":\"13f5c98b7fd5f5da\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Get Counter Value\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[1].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\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[2].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[3].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.value\",\"tot\":\"msg\"}],\"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\":[]},{\"id\":\"82aa12173dd7bbca\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Parse Simulation Values\",\"info\":\"\",\"x\":1070,\"y\":640,\"wires\":[]},{\"id\":\"57d8777e34b55b7b\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":640,\"wires\":[]},{\"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\":[{\"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\":[]},{\"id\":\"c4d4a3b0df372e4c\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"8557072f05e4bda0\",\"group\":\"af263064820fb7d0\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"Random Number\",\"label\":\"Random Number\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1350,\"y\":740,\"wires\":[]},{\"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\":[\"#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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[\"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\":[],\"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\":[{\"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\":[]},{\"id\":\"18d21607c87ab153\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Get ByteAnalogItemArray Value\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"$string(payload[0].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\":[{\"t\":\"nempty\"}],\"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\":[],\"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\":[{\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"nempty\"}],\"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\":[]},{\"id\":\"c74606c48ccf5a40\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Parse StaticData Values\",\"info\":\"\",\"x\":1070,\"y\":980,\"wires\":[]},{\"id\":\"053bda13f2a2eabe\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"name\":\"Display on Dashboard\",\"info\":\"\",\"x\":1340,\"y\":980,\"wires\":[]},{\"id\":\"277dcf430dc86996\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"ByteAnalogItemArray\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1340,\"y\":1020,\"wires\":[]},{\"id\":\"d708e6264cec0070\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"5afdbddf71507886\",\"group\":\"3d4f386e812e8b5f\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"BooleanArray\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1320,\"y\":1100,\"wires\":[]},{\"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\":[\"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\":[],\"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\":[{\"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\":[]},{\"id\":\"efb293f13af17dc1\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Get MyLevel Value\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[0].item.value\",\"tot\":\"msg\"}],\"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\":[{\"t\":\"nempty\"}],\"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\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"payload[4].item.value\",\"tot\":\"msg\"}],\"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\":[]},{\"id\":\"d4e2915ba92db8a6\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Parse MyDevice Values\",\"info\":\"\",\"x\":1040,\"y\":1220,\"wires\":[]},{\"id\":\"85619f0eab615ac7\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"25f95391088d4a08\",\"name\":\"Discard Empty Datasets\",\"info\":\"\",\"x\":800,\"y\":1240,\"wires\":[]},{\"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\":[{\"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\":[]},{\"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\":[{\"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\":[]},{\"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\":[\"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\":[[],[]]},{\"id\":\"9fa33d1c9c621611\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Dashboard Input\",\"info\":\"\",\"x\":200,\"y\":1480,\"wires\":[]},{\"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\":[[],[]]},{\"id\":\"77bcb828bec95336\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Call OPC UA Item\",\"info\":\"\",\"x\":470,\"y\":1480,\"wires\":[]},{\"id\":\"afa83dbb46449d4a\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"3de6c861611c3afa\",\"name\":\"Write OPC UA Item to Client\",\"info\":\"\",\"x\":740,\"y\":1480,\"wires\":[]},{\"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\":[\"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\":[]},{\"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\":[{\"p\":\"payload\"},{\"p\":\"topic\",\"vt\":\"str\"}],\"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\"],[]]},{\"id\":\"c7e8919b636cb51d\",\"type\":\"change\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Event Text\",\"rules\":[{\"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\":[{\"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\":[{\"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\":[]},{\"id\":\"5b4d1bd8b342fc05\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Get OPC Events from Client\",\"info\":\"\",\"x\":720,\"y\":1820,\"wires\":[]},{\"id\":\"ba1ea89438335cb8\",\"type\":\"comment\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"name\":\"Display Events on Dashboard\",\"info\":\"\",\"x\":1240,\"y\":1720,\"wires\":[]},{\"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\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1240,\"y\":1840,\"wires\":[]},{\"id\":\"0b8ac86e5e4f9f8d\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Timestamp\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1280,\"y\":1880,\"wires\":[]},{\"id\":\"62b2e14ce0429eef\",\"type\":\"ui-text\",\"z\":\"5b972161c4e0464e\",\"g\":\"a6e9abacd0bdf3b6\",\"group\":\"ec0ecb26fde8db3e\",\"order\":0,\"width\":0,\"height\":0,\"name\":\"\",\"label\":\"Latest MyLevel Event Severity\",\"format\":\"\",\"layout\":\"row-spread\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#717171\",\"className\":\"\",\"x\":1270,\"y\":1920,\"wires\":[]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow128.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-128') })</script>../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/First Pre-Alpha Release of the new Node-RED DashboardIntroducing v0.0.1 – An Initial Glimpse at the Future of Dashboarding in Node-RED2023-07-27T00:00:00ZJoe Pavitt<p>Just weeks ago, we at FlowFuse <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/">announced our plan</a> to develop a successor to the Node-RED Dashboard. Today, we're excited to reveal the pre-alpha release of this highly anticipated project, bringing us one step closer to a new era of data visualization in Node-RED.</p>
<!--more-->
<h2 id="sneak-peek-into-the-new-node-red-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/#sneak-peek-into-the-new-node-red-dashboard"></a> Sneak Peek into the New Node-RED Dashboard</h2>
<!-- -->
<p>The Node-RED Dashboard successor is now available for install as an npm package under the name <a href="https://www.npmjs.com/package/@flowforge/node-red-dashboard">@flowforge/node-red-dashboard</a> in your Node-RED palette manager.</p>
<p>This pre-alpha version includes the first set of Vue.js-based elements familiar to Node-RED Dashboard users:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-650.avif 650w, https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-650.webp 650w, https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-650.jpeg 650w, https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="new Node-RED Dashboard Elements" alt=""new Node-RED Dashboard Elements"" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr-dashboard-screenshot-SXRsRfVGhg-650.jpeg" width="1300" height="759" /></picture></p>
<p>This is but a hint of what's to come. The objective of these pre-alpha releases is to provide early access to the current status.</p>
<p>The strength of the project comes from the community. Your insights, suggestions and contributions play a significant role in shaping the future of this dashboard. Keep them coming through our <a href="https://github.com/FlowFuse/node-red-dashboard">Github page</a>.</p>
<h2 id="current-status-and-next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/dashboard-0-1-release/#current-status-and-next-steps"></a> Current Status and Next Steps</h2>
<p>As of now, we've implemented the following Dashboard Widgets:</p>
<ul>
<li>UI Text (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/38">ui-text Widget</a>)</li>
<li>Text Input (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/39">ui-text-input Widget</a>)</li>
<li>Range Slider (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/47">ui-slider Widget</a>)</li>
<li>Dropdown/Select (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/45">ui-dropdown Widget</a>)</li>
</ul>
<p>We've also introduced a new widget:</p>
<ul>
<li>Markdown (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/62">ui-markdown Widget</a>)</li>
</ul>
<p>Not yet part of the first Pre-Alpha Release:</p>
<ul>
<li>Toggle Switch (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/42">ui-switch Widget</a>)</li>
<li>Color Picker (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/46">ui-color-picker Widget</a>)</li>
<li>Number Input (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/41">ui-numeric Widget</a>)</li>
<li>Form (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/49">ui-form Widget</a>)</li>
<li>Date selector (<a href="https://github.com/FlowFuse/node-red-dashboard/issues/32">ui-date-picker</a>)</li>
</ul>
<p>Our immediate focus is to continue adding the missing elements from the original Node-RED Dashboard, releasing each as soon as they're fully developed. This will significantly increase the frequency of our releases in the upcoming weeks.</p>
<p>In addition to these releases, we plan to publish regular blog posts titled "What's New in Node-RED Dashboard". These posts will keep you informed of all the latest features, updates, and improvements.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/How to add images to Node-RED dashboards when using FlowFuseImport your images into your Node-RED dashboards, wherever you are running your instances2023-07-21T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>When designing a dashboard, images allow you to significantly enrich your content. Some examples include:</p>
<ul>
<li>
<p>displaying maps to guide engineers to a problem which needs resolving.</p>
</li>
<li>
<p>displaying pictures of specific hardware on a factory-floor which needs to be checked.</p>
</li>
<li>
<p>displaying physical tools which should be used to resolve a problem.</p>
</li>
</ul>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#prerequisites"></a> Prerequisites</h3>
<p>Before we begin, ensure you have the following custom nodes installed:</p>
<ul>
<li><a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">@flowfuse/node-red-dashboard</a> - 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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/dashboard-getting-started/">Getting started with FlowFuse Dashboarad</a>.</li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-string">node-red-contrib-string</a> - A string manipulation node based on the lightweight stringjs library.</li>
<li><a href="https://flows.nodered.org/node/node-red-node-base64">node-red-node-base64</a> - A Node-RED node to encode and decode data to and from base64.</li>
</ul>
<h2 id="easily-add-images-to-node-red-dashboards-with-flowfuse%E2%80%99s-static-asset-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#easily-add-images-to-node-red-dashboards-with-flowfuse%E2%80%99s-static-asset-service"></a> Easily Add Images to Node-RED Dashboards with FlowFuse’s Static Asset Service</h2>
<p><a href="https://flowforge.com/docs/user/static-asset-service/">FlowFuse's static assets</a> 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.</p>
<h3 id="steps-to-add-images-using-the-static-asset-service%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#steps-to-add-images-using-the-static-asset-service%3A"></a> Steps to Add Images Using the Static Asset Service:</h3>
<h5 id="1.-access-the-static-assets-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#1.-access-the-static-assets-service"></a> 1. Access the Static Assets Service</h5>
<ul>
<li>Log into your FlowFuse account, navigate to your <strong>Node-RED instance</strong>, and click on the <strong>Static Assets Service</strong> tab.</li>
</ul>
<h5 id="2.-create-a-new-folder-(optional)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#2.-create-a-new-folder-(optional)"></a> 2. Create a New Folder (Optional)</h5>
<ul>
<li>Click the <strong>New Folder</strong> button to create a folder that will help you organize your assets. Provide a folder name and <strong>confirm</strong> the creation.</li>
</ul>
<h5 id="3.-upload-your-image" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#3.-upload-your-image"></a> 3. Upload Your Image</h5>
<ul>
<li>Enter the folder (or skip this step if not using folders), click <strong>Upload</strong>, select your image file, and <strong>confirm</strong>.</li>
</ul>
<h5 id="4.-copy-the-image-path" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#4.-copy-the-image-path"></a> 4. Copy the Image Path</h5>
<ul>
<li>Once uploaded, click the <strong>copy icon</strong> next to the image to get the path for use in Node-RED.</li>
</ul>
<h5 id="5.-set-up-the-image-flow-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#5.-set-up-the-image-flow-in-node-red"></a> 5. Set Up the Image Flow in Node-RED</h5>
<ul>
<li>Open the Node-RED editor for the relevant instance.</li>
<li>Drag an <code>inject</code> node onto the canvas and set it to trigger immediately when the flow is deployed.</li>
<li>Drag a <code>read file</code> node, paste the copied file path in the <strong>Filename</strong> field, and set the output to Single buffer object.</li>
</ul>
<h5 id="6.-prepare-the-image-for-display" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#6.-prepare-the-image-for-display"></a> 6. Prepare the Image for Display</h5>
<ul>
<li>
<p>Add a <code>string</code> node to convert the buffer to a base64 string. Set <strong>From</strong> as <code>msg.filename</code> and adjust the <strong>Method</strong> to <code>getmost</code>.</p>
</li>
<li>
<p>Add a <code>change</code> node and configure it to add the elements shown in the following image:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-0wWJtSUT9P-590.avif 590w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-0wWJtSUT9P-590.webp 590w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The change node showing added elements" alt="The change node showing added elements" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-0wWJtSUT9P-590.jpeg" width="590" height="653" /></picture></p>
</li>
</ul>
<h5 id="7.-display-the-image-in-the-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#7.-display-the-image-in-the-dashboard"></a> 7. Display the Image in the Dashboard</h5>
<ul>
<li>
<p>Drag a <code>ui-template</code> node onto the canvas.</p>
</li>
<li>
<p>Add the code in <code>ui-template</code> with an <code><img></code> tag, configuring the <code>src</code> attribute with <code>msg.payload</code>, 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:</p>
<div style="position: relative" id="code-container-147">
<pre class="language-javascript"><code id="code-147" class="language-javascript"><span class="token operator"><</span>template<span class="token operator">></span><br /> <span class="token operator"><</span>Teleport v<span class="token operator">-</span><span class="token keyword">if</span><span class="token operator">=</span><span class="token string">"mounted"</span> to<span class="token operator">=</span><span class="token string">"#app-bar-title"</span><span class="token operator">></span><br /> <span class="token operator"><</span>img <span class="token operator">:</span>src<span class="token operator">=</span><span class="token string">"msg.payload"</span> style<span class="token operator">=</span><span class="token string">"height: 32px;"</span> <span class="token operator">/</span><span class="token operator">></span><br /> <span class="token operator"><</span><span class="token operator">/</span>Teleport<span class="token operator">></span><br /><span class="token operator"><</span><span class="token operator">/</span>template<span class="token operator">></span><br /><br /><span class="token operator"><</span>script<span class="token operator">></span><br /> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span><br /> <span class="token function">data</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token punctuation">{</span><br /> <span class="token literal-property property">mounted</span><span class="token operator">:</span> <span class="token boolean">false</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token function">mounted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>mounted <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /><span class="token operator"><</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-147" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
</li>
</ul>
<h5 id="8.-connect-the-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#8.-connect-the-nodes"></a> 8. Connect the Nodes</h5>
<ul>
<li>
<p>Finally connect the nodes in the following order:
the <strong>output</strong> of the <code>inject</code> node to the <strong>input</strong> of the <code>read file</code> node, then link to the <code>string node</code>, followed by the <code>change</code> node, and finally to the <code>ui-template</code> node.</p>
<p><code>inject → read file → string → change → ui-template</code></p>
</li>
</ul>
<div id="nr-flow-120" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow120 = "\n[{\"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\":[\"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\":\"<template>\\n <!-- Teleport the image to the #app-bar-title area when mounted -->\\n <Teleport v-if=\\\"mounted\\\" to=\\\"#app-bar-title\\\">\\n <img :src=\\\"msg.payload\\\" style=\\\"height: 32px;\\\" />\\n </Teleport>\\n</template>\\n\\n<script>\\n export default {\\n data() {\\n return {\\n mounted: false\\n }\\n },\\n mounted() {\\n // Set mounted to true when the component is mounted\\n this.mounted = true\\n }\\n }\\n</script>\",\"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\":[{\"p\":\"payload\"}],\"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/ff-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\":[{\"t\":\"set\",\"p\":\"mimetype\",\"pt\":\"msg\",\"to\":\"\\\"data:image/\\\"&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\":[{\"name\":\"getRightMost\",\"params\":[{\"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\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-control\",\"ui-notification\"],\"showPathInSidebar\":false,\"showPageTitle\":false,\"titleBarStyle\":\"default\"}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow120.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-120') })</script>
<p>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.</p>
<h2 id="why-not-just-store-them-in-node-red's-host-operating-system%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#why-not-just-store-them-in-node-red's-host-operating-system%3F"></a> Why not just store them in Node-RED's host operating system?</h2>
<p>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.</p>
<p>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?</p>
<h2 id="inspiration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#inspiration"></a> Inspiration</h2>
<p>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!</p>
<h2 id="solution-explanation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#solution-explanation"></a> Solution explanation</h2>
<p>There are three key sections to this solution:</p>
<ol>
<li>
<p>Pull the images we need from URLs</p>
</li>
<li>
<p>Store those images in the temporary filesystem of Node-RED</p>
</li>
<li>
<p>Serve up those images as needed in the dashboard</p>
</li>
</ol>
<p>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.</p>
<p>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.</p>
<h2 id="prequsite" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#prequsite"></a> Prequsite</h2>
<p>Before moving forward, ensure you have the following nodes installed, as the flows shared later will require them:</p>
<ul>
<li><a href="https://flows.nodered.org/node/node-red-contrib-calc">node-red-contrib-calc</a> - A Node-RED node to perform basic mathematical calculations.</li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-image-output">node-red-contrib-image-output</a> - A simple way to preview and examine images in your flows.</li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-os">node-red-contrib-os</a> - Nodes for obtaining system information like CPU usage.</li>
<li><a href="https://flows.nodered.org/node/node-red-contrib-string">node-red-contrib-string</a> - A string manipulation node based on the lightweight stringjs library.</li>
<li><a href="https://flows.nodered.org/node/@flowfuse/node-red-dashboard">@flowfuse/node-red-dashboard</a> - A set of dashboard nodes for Node-RED.</li>
<li><a href="https://flows.nodered.org/node/node-red-node-base64">node-red-node-base64</a> - A Node-RED node to encode and decode data to and from base64.</li>
</ul>
<h2 id="file-and-file-in-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#file-and-file-in-nodes"></a> File and file-in nodes</h2>
<p>I've included the flows as json below so you can try them out yourself. Please note, I'm using FlowFuse's own <a href="https://flowfuse.com/docs/user/filenodes/">file and file-in nodes</a> 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.</p>
<h2 id="the-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#the-flows"></a> The flows</h2>
<p>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:</p>
<div id="nr-flow-121" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow121 = "\n[{\"id\":\"6b8059f703d0f574\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"name\":\"Write the images to disk from the URLs\",\"style\":{\"label\":true},\"nodes\":[\"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\":[\"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\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[\\\"https://openjsf.org/wp-content/uploads/sites/84/2023/02/ff-logo-wordmark-light_4x.png\\\",\\\"https://nodered.org/images/nr-image-1.png\\\",\\\"/img/screen-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\":[\"f582702ec222069c\"],\"x\":535,\"y\":120,\"wires\":[]},{\"id\":\"8a3c077f0f85a905\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Download the images\",\"style\":{\"label\":true},\"nodes\":[\"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\":[],\"x\":460,\"y\":220,\"wires\":[[\"4d650baa2118036e\"]]},{\"id\":\"ecbd7b1a410ecd9d\",\"type\":\"change\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"Set URL to download\",\"rules\":[{\"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\":[\"8bc38803dec97185\"],\"x\":575,\"y\":220,\"wires\":[]},{\"id\":\"f582702ec222069c\",\"type\":\"link in\",\"z\":\"c6f2a894be05d857\",\"g\":\"8a3c077f0f85a905\",\"name\":\"link in 3\",\"links\":[\"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\":[\"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\":[{\"name\":\"getRightMost\",\"params\":[{\"type\":\"str\",\"value\":\"/\"}]}],\"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\":[\"4d650baa2118036e\"],\"x\":125,\"y\":320,\"wires\":[[\"95c819560d22f394\"]]},{\"id\":\"98980d88013c6e12\",\"type\":\"link out\",\"z\":\"c6f2a894be05d857\",\"g\":\"22c5026dd58e418b\",\"name\":\"link out 2\",\"mode\":\"link\",\"links\":[\"1e94b5bab542830a\"],\"x\":975,\"y\":320,\"wires\":[]},{\"id\":\"6fcca5cfee2bcb89\",\"type\":\"group\",\"z\":\"c6f2a894be05d857\",\"g\":\"6b8059f703d0f574\",\"name\":\"Output a debug once all images have been processed\",\"style\":{\"label\":true},\"nodes\":[\"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\":[]},{\"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\":[\"98980d88013c6e12\"],\"x\":105,\"y\":420,\"wires\":[[\"119f8008752bc4fb\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow121.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-121') })</script>
<p>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.</p>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/load-images-from-disk-and-show-in-dashboard-LV9FMxbbNB-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/load-images-from-disk-and-show-in-dashboard-LV9FMxbbNB-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Get the images from the local storage and place them in a dashboard" alt="Get the images from the local storage and place them in a dashboard" loading="lazy" decoding="async" src="https://flowfuse.com/img/load-images-from-disk-and-show-in-dashboard-LV9FMxbbNB-650.jpeg" width="650" height="448" /></picture></p>
<p>You can import this flow into Node-RED using the code below:</p>
<div id="nr-flow-122" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow122 = "\n[{\"id\":\"596006fe79275d55\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"name\":\"Get the images from the filestore and display in the Dashboard\",\"style\":{\"label\":true},\"nodes\":[\"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\":[\"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\":[\"a9958e1e4b07191a\"],\"x\":1305,\"y\":3940,\"wires\":[[\"ab22b978a2f5d80f\"]]},{\"id\":\"8e32aba67d9ac9d4\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"be421f8265c4c090\",\"name\":\"link out 5\",\"mode\":\"link\",\"links\":[\"bbdd3a2e737e1074\"],\"x\":1715,\"y\":3940,\"wires\":[]},{\"id\":\"d2dc98b4f3d6b968\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Prepare each image to be shown in the dashboard\",\"style\":{\"label\":true},\"nodes\":[\"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\":[{\"t\":\"set\",\"p\":\"mimetype\",\"pt\":\"msg\",\"to\":\"\\\"data:image/\\\"&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\":1810,\"y\":4040,\"wires\":[[\"2d29c953bcdab787\"]]},{\"id\":\"d856077cc6d37093\",\"type\":\"string\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"Get file type from file name\",\"methods\":[{\"name\":\"getRightMost\",\"params\":[{\"type\":\"str\",\"value\":\".\"}]}],\"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\":[\"8e32aba67d9ac9d4\"],\"x\":1305,\"y\":4040,\"wires\":[[\"d856077cc6d37093\"]]},{\"id\":\"2d29c953bcdab787\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"d2dc98b4f3d6b968\",\"name\":\"link out 6\",\"mode\":\"link\",\"links\":[\"c5e4febe48363987\"],\"x\":2055,\"y\":4040,\"wires\":[]},{\"id\":\"b9029adc6097bd14\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Send the images to the correct section of the dashboard\",\"style\":{\"label\":true},\"nodes\":[\"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\":[{\"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\"],[\"4e4105a9774c7b5c\"],[\"fb0c76a6934b0a7f\"]]},{\"id\":\"c5e4febe48363987\",\"type\":\"link in\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"name\":\"link in 6\",\"links\":[\"2d29c953bcdab787\"],\"x\":1305,\"y\":4180,\"wires\":[[\"fa873d7bccd2b6be\"]]},{\"id\":\"d80e5a54a7c5b2f8\",\"type\":\"link out\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"b9029adc6097bd14\",\"name\":\"link out 7\",\"mode\":\"link\",\"links\":[\"c9de7384c9ca672f\"],\"x\":2135,\"y\":4180,\"wires\":[]},{\"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\":\"<template>\\n <div>\\n <img :src= \\\"msg.paylaod\\\" alt=\\\"Image loaded from the filestore\\\" style=\\\"width:100%\\\"><br>\\n </div>\\n</template>\",\"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\":\"<template>\\n <div>\\n <img :src= \\\"msg.paylaod\\\" alt=\\\"Image loaded from the filestore\\\" style=\\\"width:100%\\\"><br>\\n </div>\\n</template>\",\"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\":\"<template>\\n <div>\\n <img :src= \\\"msg.paylaod\\\" alt=\\\"Image loaded from the filestore\\\" style=\\\"width:100%\\\"><br>\\n </div>\\n</template>\",\"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\":\"/\",\"icon\":\"home\",\"layout\":\"grid\",\"theme\":\"c68088445147719b\",\"breakpoints\":[{\"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\":\"/dashboard\",\"includeClientData\":true,\"acceptsClientConfig\":[\"ui-control\",\"ui-notification\"],\"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\":[\"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\":[\"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\":[]},{\"id\":\"99aaee673fc70bc2\",\"type\":\"group\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"596006fe79275d55\",\"name\":\"Inject the image files' names\",\"style\":{\"label\":true},\"nodes\":[\"11787d1ab6c8a9ec\",\"49b8f3cce48dd5a9\",\"a9958e1e4b07191a\",\"c452f250e1303c69\",\"08b30802899573a6\"],\"x\":1254,\"y\":3759,\"w\":782,\"h\":122},{\"id\":\"11787d1ab6c8a9ec\",\"type\":\"inject\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"Inject\",\"props\":[],\"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\":[\"2836914f9b643e34\"],\"x\":1995,\"y\":3800,\"wires\":[]},{\"id\":\"c452f250e1303c69\",\"type\":\"change\",\"z\":\"d4aa6dd5b63a56de\",\"g\":\"99aaee673fc70bc2\",\"name\":\"Image file names as an array\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"[\\\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow122.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-122') })</script>
<p>I have also included some simple dashboard elements you can view alongside the images. Let's take a look at the dashboard:</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/the-dashboard-Kv1WJp4RqL-646.gif 646w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The dashboard showing our images alongside other standard elements" alt="The dashboard showing our images alongside other standard elements" loading="lazy" decoding="async" src="https://flowfuse.com/img/the-dashboard-Kv1WJp4RqL-646.webp" width="646" height="656" /></picture></p>
<p>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.</p>
<h2 id="more-things-to-try" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#more-things-to-try"></a> More things to try</h2>
<p>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.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/images-in-node-red-dashboards/#conclusion"></a> Conclusion</h2>
<p>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.
I'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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/FlowFuse 1.9.3 and Device Agent 1.9.5 releasedA maintenance release to improve the Device Agent editor experience2023-07-21T00:00:00ZNick O'Leary<p>FlowFuse and the Device Agent both received updates yesterday that bring improvements to the Device Agent editor experience, making it more resilient to network issues.</p>
<!--more-->
<h2 id="improving-the-device-agent-editor-experience" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#improving-the-device-agent-editor-experience"></a> Improving the Device Agent editor experience</h2>
<p>The ability to remotely edit flows running the Device Agent has been warmly welcomed by many users on the platform. Along with that comes great feedback we can use to continue improving the user experience.</p>
<p>Some early feedback identified issues with the resilience of the tunnel we connect between the Device Agent and the platform. If the tunnel was interrupted for any reason, the user would have to manually set it up again.</p>
<p>With the FlowFuse 1.9.3 release, now running FlowFuse Cloud, along with the latest version of the Device Agent, we have made the tunnel much more resilient. It can now restablish itself without any intervention from the user - making for a much more seamless experience.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/pull/2488">#2488</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/pull/2507">#2507</a></li>
</ul>
<h2 id="other-new-features-and-bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#other-new-features-and-bug-fixes"></a> Other New Features and Bug Fixes</h2>
<ul>
<li>Fixes incorrect 'start-failed' notifications when restarting an instance <a href="https://github.com/FlowFuse/flowfuse/pull/2505">#2505</a> The system log now includes more information about the callingThe FlowFuse device agent is now supported on Windows <a href="https://github.com/FlowFuse/device-agent/issues/78">#78</a></li>
<li>Ensures the system logging captures the proper source IP address of requests <a href="https://github.com/FlowFuse/flowfuse/pull/2503">#2505</a></li>
<li>A few documentation updates, including a clarfication on how to run the Device Agent under docker <a href="https://github.com/FlowFuse/flowfuse/pull/2498">#2498</a></li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.9.3.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-3-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go the the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/Creating a Historical Data Dashboard with InfluxDB and Node-REDDetailed instructions on how to create a Node-RED dashboard that shows historical data.2023-07-18T00:00:00Z<p>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!</p>
<!--more-->
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-1-SVwKifqDBJ-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-1-SVwKifqDBJ-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the historical dashboard" alt=""Screenshot showing the historical dashboard"" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-1-SVwKifqDBJ-650.jpeg" width="650" height="598" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-2-0_I_1b9Zd8-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-2-0_I_1b9Zd8-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the historical dashboard flow" alt="Screenshot showing the historical dashboard flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-2-0_I_1b9Zd8-650.jpeg" width="650" height="184" /></picture></p>
<h2 id="capturing-serial-port-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/#capturing-serial-port-data"></a> Capturing serial port data</h2>
<p>The live view is fed by data coming from a simple scale with a serial interface. This <a href="https://www.brecknellscales.com/wp-content/uploads/2022/09/LPS-Series_u_en_fr_501724-1.pdf">Brecknell LPS-150</a> 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.</p>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/serial-port-node-3-DwwNQyF5ba-517.avif 517w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/serial-port-node-3-DwwNQyF5ba-517.webp 517w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the serial port node config" alt=""Screenshot showing the serial port node config"" loading="lazy" decoding="async" src="https://flowfuse.com/img/serial-port-node-3-DwwNQyF5ba-517.jpeg" width="517" height="679" /></picture></p>
<br />
<p>With this “delay” node, we now have a new message from the scale at a rate of 1 msg per 5 seconds</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/delay-4-827GfWRy4f-510.avif 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/delay-4-827GfWRy4f-510.webp 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the delay node properties tab" alt=""Screenshot showing the delay node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/delay-4-827GfWRy4f-510.jpeg" width="510" height="411" /></picture></p>
<br />
<p>The debugger allows us to see the raw data as it is captured.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/debugger-5-btWET_2zcA-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/debugger-5-btWET_2zcA-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Debugger" alt=""Debugger"" loading="lazy" decoding="async" src="https://flowfuse.com/img/debugger-5-btWET_2zcA-650.jpeg" width="650" height="102" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-6-k6vBLGukV1-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-6-k6vBLGukV1-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the change node setting payload" alt=""Screenshot showing the change node setting payload"" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-6-k6vBLGukV1-650.jpeg" width="650" height="304" /></picture></p>
<br />
<p>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, /-?(\d+(.\d+)?)/ , 10).match). “$match” and “/-?(\d+(.\d+)?)/” help the function pull out the numeric components of the string and “$number” parses these components to be an actual number data type.</p>
<p>Here are the properties of the “change” node.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/number-7-VUKjxQoGRn-191.avif 191w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/number-7-VUKjxQoGRn-191.webp 191w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the output type in the debug panel" alt=""Screenshot showing the output type in the debug panel"" loading="lazy" decoding="async" src="https://flowfuse.com/img/number-7-VUKjxQoGRn-191.jpeg" width="191" height="69" /></picture></p>
<h2 id="setting-up-serverless-influxdb-in-the-cloud" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/#setting-up-serverless-influxdb-in-the-cloud"></a> Setting up serverless InfluxDB in the cloud</h2>
<p>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 <a href="https://www.influxdata.com/products/influxdb-overview/">https://www.influxdata.com/products/influxdb-overview/</a> and let’s begin. Click on “Get Started for Free” under Cloud, InfluxDB Cloud Serverless.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/run-influxdb-8-dCr8iG20ob-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/run-influxdb-8-dCr8iG20ob-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Influxdb cloud 'Getting started' button" alt=""Screenshot showing the Influxdb cloud 'Getting started' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/run-influxdb-8-dCr8iG20ob-650.jpeg" width="650" height="284" /></picture></p>
<p>For this example the Free plan will work fine.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/free-influxdb-9-LrJdk7HLOv-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/free-influxdb-9-LrJdk7HLOv-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the influxdb cloud tiers" alt=""Screenshot showing the influxdb cloud tiers"" loading="lazy" decoding="async" src="https://flowfuse.com/img/free-influxdb-9-LrJdk7HLOv-650.jpeg" width="650" height="526" /></picture></p>
<p>Create a bucket to store the data.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/bucket-influxdb-10-s9YjBmiFiT-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/bucket-influxdb-10-s9YjBmiFiT-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the 'Go to buckets' button" alt=""Screenshot showing the 'Go to buckets' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/bucket-influxdb-10-s9YjBmiFiT-650.jpeg" width="650" height="482" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/load-data-influxdb-11-BwVzC3khYr-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/load-data-influxdb-11-BwVzC3khYr-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the 'Create Bucket' button" alt=""Screenshot showing the 'Create Bucket' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/load-data-influxdb-11-BwVzC3khYr-650.jpeg" width="650" height="187" /></picture></p>
<p>Generate a token to direct the calls from Node-RED to your InfluxDB account when they hit the InfluxDB server:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/token-influxdb-12-1qA96B6gb_-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/token-influxdb-12-1qA96B6gb_-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the 'Genrate token' button" alt=""Screenshot showing the 'Genrate token' button"" loading="lazy" decoding="async" src="https://flowfuse.com/img/token-influxdb-12-1qA96B6gb_-650.jpeg" width="650" height="183" /></picture></p>
<p>I selected “Generate All Access API Token,” but eventually you will want a custom, more restricted approach.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/generate-token-influxdb-13-3sTzWsuJkc-387.avif 387w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/generate-token-influxdb-13-3sTzWsuJkc-387.webp 387w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the 'prompt' asing to enter the description for token" alt=""Screenshot showing the 'prompt' asing to enter the description for token"" loading="lazy" decoding="async" src="https://flowfuse.com/img/generate-token-influxdb-13-3sTzWsuJkc-387.jpeg" width="387" height="269" /></picture></p>
<p>Copy your token and do not share it! (mine will be deleted later)</p>
<h2 id="connecting-node-red-to-influxdb" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/#connecting-node-red-to-influxdb"></a> Connecting Node-RED to InfluxDB</h2>
<p>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 <a href="https://flows.nodered.org/node/node-red-contrib-influxdb">https://flows.nodered.org/node/node-red-contrib-influxdb</a>. 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</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/install-contrib-influxdb-14-5-FBdn33Ve-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/install-contrib-influxdb-14-5-FBdn33Ve-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the influxdb node in the manage palette" alt=""Screenshot showing the influxdb node in the manage palette"" loading="lazy" decoding="async" src="https://flowfuse.com/img/install-contrib-influxdb-14-5-FBdn33Ve-650.jpeg" width="650" height="294" /></picture></p>
<br />
<p>After installing this package you will see three new nodes in your flow editor.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/influxdb-nodes-15-YhCM5CENmp-157.avif 157w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/influxdb-nodes-15-YhCM5CENmp-157.webp 157w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the installed influxdb nodes in the palette" alt="Screenshot showing the installed influxdb nodes in the palette" loading="lazy" decoding="async" src="https://flowfuse.com/img/influxdb-nodes-15-YhCM5CENmp-157.jpeg" width="157" height="143" /></picture></p>
<p>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<<THING>>” works well for initial setups to make it clear what names should go where.</THING></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/influxdb-out-node-16-BgiF3koLh3-517.avif 517w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/influxdb-out-node-16-BgiF3koLh3-517.webp 517w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the influxdb-out node config" alt=""Screenshot showing the influxdb-out node config"" loading="lazy" decoding="async" src="https://flowfuse.com/img/influxdb-out-node-16-BgiF3koLh3-517.jpeg" width="517" height="442" /></picture></p>
<br />
<p>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, “<a href="https://us-east-1-1.aws.cloud2.influxdata.com/">https://us-east-1-1.aws.cloud2.influxdata.com/</a>” and it worked. Then, enter the API token that was generated earlier.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/influxdb-node-17-tbU6XJNq3x-507.avif 507w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/influxdb-node-17-tbU6XJNq3x-507.webp 507w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="image_tooltip" alt=""image_tooltip"" loading="lazy" decoding="async" src="https://flowfuse.com/img/influxdb-node-17-tbU6XJNq3x-507.jpeg" width="507" height="362" /></picture></p>
<br />
<p>The “influxdb out” node is now ready to start storing payloads. The documentation for the InfluxDB nodes at <a href="https://flows.nodered.org/node/node-red-contrib-influxdb">https://flows.nodered.org/node/node-red-contrib-influxdb</a> 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-influxdb-out-18-sKUXhxd6sq-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-influxdb-out-18-sKUXhxd6sq-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget" alt="Screenshot showing the flow sending data to InfluxDB and to a dashboard chart widget"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-influxdb-out-18-sKUXhxd6sq-650.jpeg" width="650" height="79" /></picture></p>
<br />
<p>Here is a chart of the live data which is also being stored.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/live-data-chart-19-qkB3uPxcvV-322.avif 322w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/live-data-chart-19-qkB3uPxcvV-322.webp 322w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the dashboard with live data chart" alt=""Screenshot showing the dashboard with live data chart"" loading="lazy" decoding="async" src="https://flowfuse.com/img/live-data-chart-19-qkB3uPxcvV-322.jpeg" width="322" height="271" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-650.avif 650w, https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-650.webp 650w, https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-650.jpeg 650w, https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the InfluxDB Explorer" alt=""Screenshot showing the InfluxDB Explorer"" loading="lazy" decoding="async" src="https://flowfuse.com/img/influxdb-explorer-20-OXsksNsMfz-650.jpeg" width="1300" height="705" /></picture></p>
<br />
<h2 id="creating-a-historical-data-gui" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/#creating-a-historical-data-gui"></a> Creating a historical data GUI</h2>
<p>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.</p>
<p>Here is the Dashboard group we will create for this GUI.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/historical-data-21-0AcJTMFOcD-320.avif 320w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/historical-data-21-0AcJTMFOcD-320.webp 320w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the GUI for historical data" alt=""Screenshot showing the GUI for historical data"" loading="lazy" decoding="async" src="https://flowfuse.com/img/historical-data-21-0AcJTMFOcD-320.jpeg" width="320" height="600" /></picture></p>
<p>And here is the flow to create it.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/data-flow-22-x9hE0kmJsf-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/data-flow-22-x9hE0kmJsf-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the flow of historical data GUI" alt=""Screenshot showing the flow of historical data GUI."" loading="lazy" decoding="async" src="https://flowfuse.com/img/data-flow-22-x9hE0kmJsf-650.jpeg" width="650" height="116" /></picture></p>
<p>A “template” node creates a convenient way to create a plain text output with variable properties within. Below you can see that <code>msg.query</code> 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: <a href="https://docs.influxdata.com/influxdb/v2.0/query-data/get-started/query-influxdb/">https://docs.influxdata.com/influxdb/v2.0/query-data/get-started/query-influxdb/</a>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/template-node-23-ZzMWqGgjt8-640.avif 640w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/template-node-23-ZzMWqGgjt8-640.webp 640w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the template node" alt=""Screenshot showing the template node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/template-node-23-ZzMWqGgjt8-640.jpeg" width="640" height="667" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/form-node-24-gM4bqn-p2V-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/form-node-24-gM4bqn-p2V-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the form widget" alt=""Screenshot of the form widget"" loading="lazy" decoding="async" src="https://flowfuse.com/img/form-node-24-gM4bqn-p2V-650.jpeg" width="650" height="563" /></picture></p>
<p>Here is the code from the “time/date” 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/function-node-25-0bAZwBvHvW-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/function-node-25-0bAZwBvHvW-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the function node properties tab" alt=""Screenshot of the function node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/function-node-25-0bAZwBvHvW-650.jpeg" width="650" height="466" /></picture></p>
<br />
<p>Here is the “change” node used to create the msg.rangeEnd. The JSONatta expression is <code>$fromMillis($toMillis(msg.rangeStart) + msg.payload.window * 60 * 1000)</code>. The expression combines the milliseconds from the <code>msg.rangeStart</code> with the calculated milliseconds in the “Window (minutes)” from the GUI.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-26-tzOj3AJFAa-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-26-tzOj3AJFAa-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the change node properties tab" alt=""Screenshot of the change node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-26-tzOj3AJFAa-650.jpeg" width="650" height="391" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/data-format-27-OFKpV992TT-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/data-format-27-OFKpV992TT-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the formated data in the debug panel" alt=""Screenshot showing the formated data in the debug panel"" loading="lazy" decoding="async" src="https://flowfuse.com/img/data-format-27-OFKpV992TT-650.jpeg" width="650" height="579" /></picture></p>
<p>Rob Marcer has a great article on working with persistent chart data found here: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/">/blog/2023/05/persisting-chart-data-in-node-red/</a>.</p>
<p>We can use a series of nodes from the Node-RED core package to transform this data.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/transform-data-28-SUZ7P-iTlR-529.avif 529w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/transform-data-28-SUZ7P-iTlR-529.webp 529w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the flow to transform the data" alt=""Screenshot showing the flow to transform the data"" loading="lazy" decoding="async" src="https://flowfuse.com/img/transform-data-28-SUZ7P-iTlR-529.jpeg" width="529" height="269" /></picture></p>
<br />
<p>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.”</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/switch-node-28-oyYAuKY4zP-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/switch-node-28-oyYAuKY4zP-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the switch node properties tab" alt=""Screenshot of the switch node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/switch-node-28-oyYAuKY4zP-650.jpeg" width="650" height="606" /></picture></p>
<br />
<p>The “Label” field in the “chart” node can also be dynamically created with the mustache syntax.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/chart-node-29-8PODDPN9UY-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/chart-node-29-8PODDPN9UY-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the chart widget setting label dynamically" alt=""Screenshot of the chart widget setting label dynamically"" loading="lazy" decoding="async" src="https://flowfuse.com/img/chart-node-29-8PODDPN9UY-650.jpeg" width="650" height="428" /></picture></p>
<br />
<p>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 <code>msg.title</code> to “No Data” so users know their query, though successful, returned an empty set of values.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-30-r1fwGZMnTs-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-30-r1fwGZMnTs-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the change node properties tab" alt=""Screenshot of the change node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-30-r1fwGZMnTs-650.jpeg" width="650" height="606" /></picture></p>
<br />
<p>The parameters for the “split” node can be left as-is.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/split-node-31-IQf-ZExcu2-608.avif 608w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/split-node-31-IQf-ZExcu2-608.webp 608w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the split node properties tab" alt=""Screenshot of the split node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/split-node-31-IQf-ZExcu2-608.jpeg" width="608" height="480" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-32-smuQTyksre-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-32-smuQTyksre-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the change node properties tab" alt=""Screenshot of the change node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-32-smuQTyksre-650.jpeg" width="650" height="486" /></picture></p>
<br />
<p>The “join” node just needs to be set to “Combine each” msg.chartData object and configured “to create” an array.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/join-node-33-55a-Ouf-0x-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/join-node-33-55a-Ouf-0x-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the join node properties tab" alt=""Screenshot of the join node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/join-node-33-55a-Ouf-0x-650.jpeg" width="650" height="426" /></picture></p>
<br />
<p>The final “change” node, “format,” is where we prescribe the format needed for the “chart” node, [{"series":[""],"data":[[]],"labels":[""]}], and finally we insert our <code>msg.chartData</code> array into that structure. Notice <code>msg.title</code> is now set to “Data Received.”</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/change-node-33-gUhHgghB58-595.avif 595w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/change-node-33-gUhHgghB58-595.webp 595w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the change node properties tab" alt=""Screenshot of the change node properties tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/change-node-33-gUhHgghB58-595.jpeg" width="595" height="451" /></picture></p>
<br />
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/final-dashboard-34-k8pk61PD73-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/final-dashboard-34-k8pk61PD73-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the dashboard showing the historical and live data chart" alt=""Screenshot of the dashboard showing the historical and live data chart"" loading="lazy" decoding="async" src="https://flowfuse.com/img/final-dashboard-34-k8pk61PD73-650.jpeg" width="650" height="604" /></picture></p>
<p>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:</p>
<h2 id="final-thoughts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/influxdb-historical-data/#final-thoughts"></a> Final Thoughts</h2>
<p>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.</p>
<p>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/7? How do you deploy it to other locations? What happens if someone accidentally breaks the flow?</p>
<p>This is where <strong>FlowFuse</strong> comes in. It takes your existing Node-RED flows and adds:</p>
<ul>
<li>Automatic backups and instant recovery</li>
<li>Easy deployment to multiple locations</li>
<li>Version control for safe and trackable updates</li>
<li>Team collaboration without conflicts</li>
<li>Remote instance management—and much more</li>
</ul>
<p>Your flows continue to work just as they are—FlowFuse simply makes them production-ready.</p>
<p><a href="https://app.flowfuse.com/account/create">Try FlowFuse free for 30 days</a> and see how it transforms Node-RED into a scalable, enterprise-ready platform.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/How to Deploy a Basic OPC-UA Server in Node-RED - Part 1OPC-UA Server Information Modeling in Node-RED2023-07-13T00:00:00Z<p>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.</p>
<!--more-->
<h2 id="what-is-opc-ua%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/#what-is-opc-ua%3F"></a> What is OPC-UA?</h2>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-650.avif 650w, https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-650.webp 650w, https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-650.jpeg 650w, https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Automation Pyramid" alt=""Automation Pyramid"" loading="lazy" decoding="async" src="https://flowfuse.com/img/automation-pyramid-6xEWe3SHv7-650.jpeg" width="1300" height="934" /></picture>
<em>Image reference - <a href="https://www.motioncontroltips.com/what-is-opc-ua-and-how-does-it-compare-with-industrial-ethernet/">imagecontroltips.com</a></em></p>
<h2 id="fieldbus-model-vs-opc-ua-information-model" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/#fieldbus-model-vs-opc-ua-information-model"></a> Fieldbus Model vs OPC-UA Information Model</h2>
<p>As of today, industrial ethernet fieldbuses dominate the field/device-level (level 0) and controller/PLC-level (level 1) of the automation pyramid.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/OPC-UA-pyramid-2-tU8VywnGaC-630.avif 630w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/OPC-UA-pyramid-2-tU8VywnGaC-630.webp 630w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="OPC-UA Pyramid" alt=""OPC-UA Pyramid"" loading="lazy" decoding="async" src="https://flowfuse.com/img/OPC-UA-pyramid-2-tU8VywnGaC-630.jpeg" width="630" height="460" /></picture>
<em>Image reference - <a href="https://www.mdpi.com/1424-8220/21/14/4656">mdpi.com</a></em></p>
<p>Fieldbuses such as Profinet, Ethernet/IP, 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 <a href="https://www.tttech-industrial.com/resource-library/blog-posts/opc-ua-fx">Time Sensitive Networking (TSN) into the OPC-UA technology stack</a>, OPC-UA can be feasibly used for real-time communication all the way down to the device level.</p>
<p>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).</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-650.avif 650w, https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-650.webp 650w, https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-650.jpeg 650w, https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Fieldbus Model" alt=""Fieldbus Model"" loading="lazy" decoding="async" src="https://flowfuse.com/img/fieldbus-model-NtyAEOfJEA-650.jpeg" width="1300" height="654" /></picture></p>
<p>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 <a href="lhttps://reference.opcfoundation.org/Core/Part5/v104/docs/">OPC Information model</a>, and consists of pre-defined classes and methods that are programmed in the OPC Server address space.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-information-model-78prN7R6B0-650.avif 650w, https://flowfuse.com/img/opc-information-model-78prN7R6B0-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-information-model-78prN7R6B0-650.webp 650w, https://flowfuse.com/img/opc-information-model-78prN7R6B0-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/opc-information-model-78prN7R6B0-650.jpeg 650w, https://flowfuse.com/img/opc-information-model-78prN7R6B0-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="OPC Information Model" alt=""OPC Information Model"" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-information-model-78prN7R6B0-650.jpeg" width="1300" height="790" /></picture>
Devices 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:</p>
<p><code>/Root/Objects/Calcinator 1 PLC/Temperature Transmitters/Tank 1 Temperature/Transmitter Value</code></p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-client-browser-xaVrhfTOIm-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-client-browser-xaVrhfTOIm-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="OPC Information Model" alt=""OPC Client Browser"" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-client-browser-xaVrhfTOIm-650.jpeg" width="650" height="485" /></picture>
In 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/OPC-UA-distributed-model-t4CxB7ZRBs-525.avif 525w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/OPC-UA-distributed-model-t4CxB7ZRBs-525.webp 525w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="OPC-UA Distributed Model" alt=""OPC-UA Distributed Model"" loading="lazy" decoding="async" src="https://flowfuse.com/img/OPC-UA-distributed-model-t4CxB7ZRBs-525.jpeg" width="525" height="350" /></picture>
<em>Image reference - <a href="https://ifr.org/post/faster-robot-communication-through-the-opc-robotics-companion-specification">ifr.org</a></em></p>
<p>The OPC client simply needs to subscribe to the OPC Server endpoint url (ex. opc.tcp://server.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.</p>
<h2 id="deploying-an-example-opc-ua-server-in-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/#deploying-an-example-opc-ua-server-in-node-red"></a> Deploying an Example OPC-UA Server in Node-RED</h2>
<p>With some background on OPC-UA and how information is modeled in mind, we can take a look at the <a href="https://flows.nodered.org/node/node-red-contrib-opcua-server">node-red-contrib-opcua-server</a> node, which is merely a compact version of the <a href="https://flows.nodered.org/node/node-red-contrib-opcua">node-red-contrib-opcua</a> node that only focuses on the OPC-UA server and hence requires less dependencies.</p>
<p>An <a href="https://github.com/BiancoRoyal/node-red-contrib-opcua-server/blob/master/examples/server-with-context.json">example flow</a> 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.</p>
<p>Deploying the example flow yields the following result -</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-650.avif 650w, https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-650.webp 650w, https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-650.jpeg 650w, https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Compact Server Flow" alt="Compact Server Flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-server-flow-vnx5mwUMDT-650.jpeg" width="1300" height="199" /></picture></p>
<ul>
<li>an inject node is trigging the function <code>set flow context Inputs</code> at a one second interval, which creates 7 randomly generated float values and stores them as flow context variables, <code>isoInput2</code> - <code>isoInput8</code> (isolated inputs). The values will change to a new random number each time the node is injected.</li>
</ul>
<div style="position: relative" id="code-container-74">
<pre class="language-javascript"><code id="code-74" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput2'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">12.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput3'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">13.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput4'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">14.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput5'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">15.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput6'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">16.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput7'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">17.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoInput8'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">18.0</span><span class="token punctuation">)</span><br /><br /><span class="token operator">...</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-74" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<ul>
<li>another inject node is triggering the function <code>set flow context Outputs</code>, also at a one second interval, which creates another set of 7 randomly generated float values and stores them as flow context variables, <code>isoOutput2</code> - <code>isoOutput8</code> (isolated inputs). The values will change to a new random number each time the node is injected.</li>
</ul>
<div style="position: relative" id="code-container-82">
<pre class="language-javascript"><code id="code-82" class="language-javascript">flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput2'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">2.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput3'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">3.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput4'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">4.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput5'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">5.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput6'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">6.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput7'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">7.0</span><span class="token punctuation">)</span><br />flow<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'isoOutput8'</span><span class="token punctuation">,</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">8.0</span><span class="token punctuation">)</span><br /><br /><span class="token operator">...</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-82" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>We can confirm the values are being stored in memory by checking the flow context data and pressing the refresh button.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/context-data-1-IczPgYP8Hd-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/context-data-1-IczPgYP8Hd-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Context Data option" alt=""Screenshot showing the Context Data option"" loading="lazy" decoding="async" src="https://flowfuse.com/img/context-data-1-IczPgYP8Hd-650.jpeg" width="650" height="433" /></picture></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/context-data-2-wZaRYwhx8v-617.avif 617w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/context-data-2-wZaRYwhx8v-617.webp 617w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the flow variables in the context data tab" alt="Screenshot showing the flow variables in the context data tab" loading="lazy" decoding="async" src="https://flowfuse.com/img/context-data-2-wZaRYwhx8v-617.jpeg" width="617" height="1504" /></picture>
Each time we hit refresh, the values change, confirming that the values are randomly changing every second.</p>
<p>The last, and most important part of the flow, is the <code>Compact-Server</code> node, which actually stands alone without any incoming or outgoing connections.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-server-node-FczbW0zSos-384.avif 384w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-server-node-FczbW0zSos-384.webp 384w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Compact Server Node" alt=""Compact Server Node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-server-node-FczbW0zSos-384.jpeg" width="384" height="203" /></picture>
In the <code>Compact-Server</code> node properties, the first tab is <code>Settings</code>, and the two important properties here are <code>Port</code> and <code>Show Errors</code>. As can be seen in the node screenshot above, the node is reporting <code>active</code>, which means the server is configured correctly.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/settings-tab-dggoF-GT-9-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/settings-tab-dggoF-GT-9-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Settings Tab of compact server node" alt="Screenshot showing the Settings Tab of compact server node" loading="lazy" decoding="async" src="https://flowfuse.com/img/settings-tab-dggoF-GT-9-650.jpeg" width="650" height="763" /></picture></p>
<p>The <code>Limits</code> tab specifies some default limits that we can configure if we like, but are not necessary to be modified for test purposes.</p>
<p>The <code>Security</code> tab has one important option, <code>Allow Anonymous</code>. By default, anonymous access is enabled.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/security-tab-1iBS79iJLd-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/security-tab-1iBS79iJLd-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Security Tab of compact server node" alt="Screenshot showing the Security Tab of compact server node" loading="lazy" decoding="async" src="https://flowfuse.com/img/security-tab-1iBS79iJLd-650.jpeg" width="650" height="462" /></picture>
For a production system, we will want to enable security, but for test purposes, we will leave anonymous access enabled.</p>
<p><code>Users & Sets</code> tab is related to security and permissions. We can leave this empty for testing.</p>
<p>The <code>Address Space</code> tab is where our server OPC Information Model is constructed, using classes and methods from the <a href="https://node-opcua.github.io/">node-opcua sdk</a>.</p>
<p>Breaking down the provided example code for further context, it starts with a function that is responsible for invoking the OPC-UA server,</p>
<div style="position: relative" id="code-container-119">
<pre class="language-javascript"><code id="code-119" class="language-javascript"> <span class="token keyword">const</span> opcua <span class="token operator">=</span> coreServer<span class="token punctuation">.</span>choreCompact<span class="token punctuation">.</span>opcua<span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-119" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>and then the namespace is created.</p>
<div style="position: relative" id="code-container-123">
<pre class="language-javascript"><code id="code-123" class="language-javascript"><span class="token keyword">const</span> namespace <span class="token operator">=</span> addressSpace<span class="token punctuation">.</span><span class="token function">getOwnNamespace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-123" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Further down, the variables that will be published by the server (which are our <code>isoInput</code> & <code>isoOutput</code> flow context variables) are initialized,</p>
<div style="position: relative" id="code-container-127">
<pre class="language-javascript"><code id="code-127" class="language-javascript"> <span class="token keyword">this</span><span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"isoInput1"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br /> flexServerInternals<span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><br /> <span class="token string">"isoInput1"</span><span class="token punctuation">,</span><br /> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">50.0</span><br /> <span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"isoInput2"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">this</span><span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"isoInput3"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token operator">...</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-127" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>and an OPC folder structure is defined.</p>
<div style="position: relative" id="code-container-131">
<pre class="language-javascript"><code id="code-131" class="language-javascript"> coreServer<span class="token punctuation">.</span><span class="token function">debugLog</span><span class="token punctuation">(</span><span class="token string">"init dynamic address space"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">const</span> rootFolder <span class="token operator">=</span> addressSpace<span class="token punctuation">.</span><span class="token function">findNode</span><span class="token punctuation">(</span><span class="token string">"RootFolder"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> node<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"construct new address space for OPC UA"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">const</span> myDevice <span class="token operator">=</span> namespace<span class="token punctuation">.</span><span class="token function">addFolder</span><span class="token punctuation">(</span>rootFolder<span class="token punctuation">.</span>objects<span class="token punctuation">,</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"browseName"</span><span class="token operator">:</span> <span class="token string">"RaspberryPI-Zero-WLAN"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><span class="token operator">...</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-131" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Then, with our variables and folder structure defined, nodes are added to the namespace for each context variable.</p>
<div style="position: relative" id="code-container-135">
<pre class="language-javascript"><code id="code-135" class="language-javascript"> <span class="token keyword">const</span> gpioDI1 <span class="token operator">=</span> namespace<span class="token punctuation">.</span><span class="token function">addVariable</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token string-property property">"organizedBy"</span><span class="token operator">:</span> isoInputs<span class="token punctuation">,</span><br /> <span class="token string-property property">"browseName"</span><span class="token operator">:</span> <span class="token string">"I1"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"nodeId"</span><span class="token operator">:</span> <span class="token string">"ns=1;s=Isolated_Input1"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"dataType"</span><span class="token operator">:</span> <span class="token string">"Double"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"value"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br /> <span class="token string-property property">"get"</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Variant</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token string-property property">"dataType"</span><span class="token operator">:</span> DataType<span class="token punctuation">.</span>Double<span class="token punctuation">,</span><br /> <span class="token string-property property">"value"</span><span class="token operator">:</span> flexServerInternals<span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"isoInput1"</span><span class="token punctuation">)</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"set"</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">variant</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br /> flexServerInternals<span class="token punctuation">.</span>sandboxFlowContext<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><br /> <span class="token string">"isoInput1"</span><span class="token punctuation">,</span><br /> <span class="token function">parseFloat</span><span class="token punctuation">(</span>variant<span class="token punctuation">.</span>value<span class="token punctuation">)</span><br /> <span class="token punctuation">)</span><span class="token punctuation">;</span><br /> <span class="token keyword">return</span> opcua<span class="token punctuation">.</span>StatusCodes<span class="token punctuation">.</span>Good<span class="token punctuation">;</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token operator">...</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-135" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Last, OPC views are defined. Views create custom hierarchies our OPC Client can browse as an alternative to the default folder structure.</p>
<div style="position: relative" id="code-container-139">
<pre class="language-javascript"><code id="code-139" class="language-javascript"> <span class="token keyword">const</span> viewDI <span class="token operator">=</span> namespace<span class="token punctuation">.</span><span class="token function">addView</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token string-property property">"organizedBy"</span><span class="token operator">:</span> rootFolder<span class="token punctuation">.</span>views<span class="token punctuation">,</span><br /> <span class="token string-property property">"browseName"</span><span class="token operator">:</span> <span class="token string">"RPIW0-Digital-Ins"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> <span class="token keyword">const</span> viewDO <span class="token operator">=</span> namespace<span class="token punctuation">.</span><span class="token function">addView</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token string-property property">"organizedBy"</span><span class="token operator">:</span> rootFolder<span class="token punctuation">.</span>views<span class="token punctuation">,</span><br /> <span class="token string-property property">"browseName"</span><span class="token operator">:</span> <span class="token string">"RPIW0-Digital-Outs"</span><br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /> viewDI<span class="token punctuation">.</span><span class="token function">addReference</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br /> <span class="token string-property property">"referenceType"</span><span class="token operator">:</span> <span class="token string">"Organizes"</span><span class="token punctuation">,</span><br /> <span class="token string-property property">"nodeId"</span><span class="token operator">:</span> gpioDI1<span class="token punctuation">.</span>nodeId<br /> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br /><br /><span class="token operator">...</span><br /></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-139" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Finally, on the <code>Discovery</code> tab, we must define an endpoint for an OPC Client to subscribe to.</p>
<p>The <code>Endpoint Url</code> follows the format <code>opc.tcp://<address>:port</code>. Our port was defined on the <code>Settings</code> tab, which by default, is port <code>54845</code>. 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 = <code>opc.tcp://192.168.0.114:54845</code></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-650.avif 650w, https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-650.webp 650w, https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-650.jpeg 650w, https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Discovery Tab of compact server node" alt="Screenshot showing the Discovery Tab of compact server node" loading="lazy" decoding="async" src="https://flowfuse.com/img/discovery-tab-IZqHlj2fy9-650.jpeg" width="1300" height="624" /></picture>
Once the endpoint url is added, deploy the flow, and confirm the server is reporting “active”.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-server-active-i9AeSVwBwT-427.avif 427w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-server-active-i9AeSVwBwT-427.webp 427w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Active Tab of compact server node" alt="Screenshot showing the Active Tab of compact server node" loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-server-active-i9AeSVwBwT-427.jpeg" width="427" height="230" /></picture></p>
<h2 id="connect-to-example-opc-server-using-opc-ua-browser" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/#connect-to-example-opc-server-using-opc-ua-browser"></a> Connect to Example OPC-Server Using OPC-UA Browser</h2>
<p>To connect to our OPC endpoint, we need an OPC Client. Prosys provides a <a href="https://www.prosysopc.com/products/opc-ua-browser/">free OPC-UA Browser </a>that supports Windows, Linux, and Mac OS. To test our Server, the Windows version of Prosys OPC-UA Browser will be utilized.</p>
<p>To connect to our Node-RED OPC server, enter the endpoint url and press “connect to server”.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-650.avif 650w, https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-650.webp 650w, https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-650.jpeg 650w, https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the OPC Client" alt=""Screenshot showing the OPC Client"" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-client-connect-rB-AxlG9NI-650.jpeg" width="1300" height="150" /></picture>
It will ask for security. Remember that we allowed anonymous access, so the default security mode of <code>None</code> is the correct option.</p>
<p>Once connected, we can browse our OPC Server.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-650.avif 650w, https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-650.webp 650w, https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-650.jpeg 650w, https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="OPC Client UI" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-client-ui-Z0GIgYvSCm-650.jpeg" width="1300" height="814" /></picture>
If we navigate to <code>Objects → RaspberryPI-Zero-WLAN → GPIO → Inputs</code>, we can see a list of inputs that correspond to the <code>isoInput</code> context variables defined in the example flow, which are randomly generated numbers.</p>
<p>Clicking <code>I1</code> we can see the value in real-time, along with some additional properties.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-client-node-PEU8njC0YQ-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-client-node-PEU8njC0YQ-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="OPC Client Node" alt="OPC Client Node" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-client-node-PEU8njC0YQ-650.jpeg" width="650" height="466" /></picture>
If we go to <code>Views</code>, we can see the custom hierarchy defined in the example server, which divides the data by Digital-Ins and Digital-Outs.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/opc-client-view-g1n1EAuaf5-472.avif 472w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/opc-client-view-g1n1EAuaf5-472.webp 472w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="OPC Client View" loading="lazy" decoding="async" src="https://flowfuse.com/img/opc-client-view-g1n1EAuaf5-472.jpeg" width="472" height="683" /></picture></p>
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/how-to-deploy-a-basic-opc-ua-server-in-node-red/#summary"></a> Summary</h2>
<p>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.</p>
<p>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/IP, using the PLC data to develop a custom OPC UA Information Model programmed in the OPC server address space.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/Community News July 2023Your monthly update for the FlowFuse and Node-RED communities2023-07-12T00:00:00Z<p>Welcome to the FlowFuse newsletter for July 2023, a monthly roundup of what’s been happening with FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="new-release" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#new-release"></a> New Release</h2>
<p>Last week we released FlowFuse 1.9, featuring new API documentation available in the Swagger UI and the ability to customize Node-RED palettes.</p>
<p>Read about the details of FlowFuse 1.9 in our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/">release announcement</a>.</p>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#upcoming-events"></a> Upcoming events</h2>
<h3 id="how-to-deploy-node-red-to-hundreds-of-plcs-and-iot-edge-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#how-to-deploy-node-red-to-hundreds-of-plcs-and-iot-edge-devices"></a> How to deploy Node-RED to hundreds of PLCs and IoT edge devices</h3>
<p>Our next webinar will be focused on the device management capabilities in the FlowFuse platform. Lots of companies are deploying Node-RED to PLCs and IIoT edge computers. FlowFuse allows these companies to scale and manage Node-RED deployment out to hundreds of these types of devices. Discover how during our next webinar.</p>
<p><a href="https://flowfuse.com/webinars/2023/flowforge-device-management/">Sign-up today</a> to join us on July 27.</p>
<h2 id="from-our-blog-and-documentation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#from-our-blog-and-documentation"></a> From our Blog and Documentation</h2>
<ul>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/">The Next Step in Data Visualization - Announcing the Successor to the Node-RED Dashboard</a> - FlowFuse announces plans to develop the next version of the Node-RED Dashboard project.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/">Node-RED as a No-Code Ethernet/IP to S7 Protocol Converter</a> - A guide to using Node-RED for converting ethernet IP data to Siemens S7. Also a <a href="https://youtu.be/dteXgcBXUnk">video version</a> of the same content.</p>
</li>
<li>
<p><a href="https://flowfuse.com/node-red/protocol/mqtt/">MQTT and its Role in IoT and Industrial IoT</a> - A practical explainer on the role of MQTT in IoT use cases and how to connect with an MQTT broker in Node-RED.</p>
</li>
<li>
<p>Two new Node-RED Nodes Explained articles</p>
<ul>
<li><a href="https://flowfuse.com/node-red/core-nodes/split/">Nodes explained: Split</a></li>
<li><a href="https://flowfuse.com/node-red/core-nodes/filter/">Nodes explained: Filter</a></li>
</ul>
</li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#from-the-community"></a> From the Community</h2>
<ul>
<li>
<p><a href="https://youtu.be/1GKkXJOQMhU">Node-RED & Industry 4.0: The Future is Now</a> - An informative panel discussion led by Walker Reynolds on the role of Node-RED in the future of Industry 4.0.</p>
</li>
<li>
<p><a href="http://blog.openmindmap.org/blog/node-red-terminology/">Node-RED Terminology</a> - An explanation of the different Node-RED terminology.</p>
</li>
</ul>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/community-news-07/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">Contract Front-End Engineer – Node-RED Dashboard</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/FlowFuse now offers API Documentation with Swagger UIFlowFuse 1.9 adds new features to make it easier to administer FlowFuse2023-07-06T00:00:00Z<p>FlowFuse 1.9 adds new features to make it easier to administer FlowFuse platform deployments, including new API documentation and the ability to create customized Node-RED palettes.</p>
<!--more-->
<h2 id="api-documentation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#api-documentation"></a> API Documentation</h2>
<p>FlowFuse API allows developers to programmatically interact with the FlowFuse platform. This makes it possible to integrate FlowFuse into different infrastructure technologies, create scripts to automate specific FlowFuse tasks and embed FlowFuse into other applications.</p>
<p>In the 1.9 release we are now publishing our <a href="https://flowfuse.com/docs/api/">API documentation</a> using the <a href="https://swagger.io/specification/">OpenAPI specification</a> and making it viewable with the Swagger UI. Both these industrial standards will make using the FlowFuse API easier to use and understand.</p>
<h2 id="customize-node-red-palettes-%232002" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#customize-node-red-palettes-%232002"></a> Customize Node-RED Palettes <a href="https://github.com/FlowFuse/flowfuse/issues/2002">#2002</a></h2>
<p>FlowFuse platform adminstrators are now able to create customized Node-RED palettes that will be used when a Node-RED instance is created. An adminstator can create pre-defined templates to specify the nodes that should be included in the palette. This makes it easier for FlowFuse teams to standardized on Node-RED usage across an organization.</p>
<p>Note: this feature is not available for FlowFuse cloud users since they do not have administrator access.</p>
<h2 id="new-rbac-role-for-dashboard-users-%232292" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#new-rbac-role-for-dashboard-users-%232292"></a> New RBAC Role for Dashboard users <a href="https://github.com/FlowFuse/flowfuse/issues/1924">#2292</a></h2>
<p>A new FlowFuse user role has been created to view Node-RED dashboards. This allows for users to view Nod-RED dashboards without access to the Node-RED editor or requiring separate login credentials.</p>
<h2 id="other-new-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#other-new-features"></a> Other New Features</h2>
<ul>
<li>FlowFuse device agent is now supported on Windows <a href="https://github.com/FlowFuse/device-agent/issues/78">#78</a></li>
<li>Allow local configuration of https/httpStatic on a device <a href="https://github.com/FlowFuse/device-agent/issues/110">#110</a></li>
<li>Implementing custom certificate settings for device configuration <a href="https://github.com/FlowFuse/flowfuse/issues/2257">#2257</a></li>
<li>Allow devices to access the "Snapshot ID" and the "Snapshot Name" running on them <a href="https://github.com/FlowFuse/device-agent/issues/94">#94</a></li>
<li>High Availability logging enhanced: Individual Node-RED Instance replica querying and filtering <a href="https://github.com/FlowFuse/flowfuse/issues/2260">#2260</a></li>
<li>High Availability is now generally available <a href="https://github.com/FlowFuse/flowfuse/issues/2412">#2414</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#bug-fixes"></a> Bug Fixes</h2>
<ul>
<li>Can not promote NR instance in DevOps Pipeline <a href="https://github.com/FlowFuse/flowfuse/issues/2363">#2363</a></li>
<li>Billing team menu item missing on first page load <a href="https://github.com/FlowFuse/flowfuse/issues/2398">#2398</a></li>
<li>Duplicate labels in Instance Import dialog <a href="https://github.com/FlowFuse/flowfuse/issues/2200">#2200</a></li>
<li>Broken littie animations <a href="https://github.com/FlowFuse/flowfuse/issues/2354">#2354</a></li>
<li>Instance Logs page doesn't handle errors well <a href="https://github.com/FlowFuse/flowfuse/issues/1083">#1083</a></li>
<li>Device continues to run edited flows once taken out of dev mode <a href="https://github.com/FlowFuse/flowfuse/issues/2323">#2323</a></li>
<li>Device Editor cannot pickup FF theme <a href="https://github.com/FlowFuse/device-agent/issues/89">#89</a></li>
</ul>
<h2 id="community-contributions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#community-contributions"></a> Community Contributions</h2>
<p>Thanks to our community members for their contributions to this release.</p>
<ul>
<li>sumitshinde-84 - make Instance and application names in delete popup easily selectable <a href="https://github.com/FlowFuse/flowfuse/pull/2291">#2291</a></li>
<li>biancode - Fixed typo in doc <a href="https://github.com/FlowFuse/flowfuse/pull/2327">#2327</a></li>
</ul>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.9.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/07/flowforge-1-9-release/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go the the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/The Next Step in Data Visualization - Announcing the Successor to the Node-RED DashboardFlowFuse's Journey Towards a New Node-RED Dashboard2023-06-21T00:00:00ZJoe Pavitt<p>For the past several years, the Node-RED Dashboard has been an indispensable tool for many Node-RED users. It has offered a seamless way to create live dashboards, enabling the quick and intuitive creation of user interfaces for Node-RED flows. However, as the saying goes, "all good things must come to an end."</p>
<!--more-->
<p>We at FlowFuse have identified a significant need for a modern, interactive data visualization and dashboard solution. Having evaluated a wide range of options, we've decided to embark on an exciting journey: the creation of what we hope becomes the official successor to the Node-RED Dashboard.</p>
<h2 id="the-problem-at-hand" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/#the-problem-at-hand"></a> The Problem at Hand</h2>
<p>The original Node-RED Dashboard is based on Angular v1, which is no longer maintained. Although small patches have been and will continue to be applied on a "best can do" basis, there will be no major feature upgrades. The lack of ongoing maintenance and updates has the potential to lead to underlying security breakages, a risk we are not comfortable taking. We have recognized the need to innovate and adapt, which is why we are creating a completely new project to replace the existing Node-RED Dashboard.</p>
<h2 id="the-solution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/#the-solution"></a> The Solution</h2>
<p>The successor to the Node-RED Dashboard will be a completely new project, published as a separate package. This new project will take the reins from the old dashboard and guide us into the future with the support and blessing of the existing dashboard maintainers. The Node-RED community can rest assured that the project will stay under the Apache 2.0 licence and keep intact the core principles of open-source and community-driven development.</p>
<h2 id="community-involvement-and-open-source-contribution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/#community-involvement-and-open-source-contribution"></a> Community Involvement and Open Source Contribution</h2>
<p>We believe in the power of the community, and we want your feedback. If there are features you'd like to see or improvements you think can be made, we invite you to open a <a href="https://github.com/FlowFuse/node-red-dashboard/issues/new/choose">Github issue</a>. Your insights and suggestions will be invaluable in shaping the future of this project. Contributions from the community are not just welcome, but highly encouraged. We believe that the strength of a project is proportional to the strength of its community.</p>
<p>In our commitment to transparency and collaboration, we will be documenting all major decision-making processes and sharing the details here in our blog as they become available. By working closely with additional Node-RED key figures like Dave Conway-Jones, and by making our development process as open and collaborative as possible, we aim to ensure that the successor to the Node-RED Dashboard lives up to the high standards set by the original, while also introducing innovative features and enhancements.</p>
<h2 id="join-the-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/#join-the-team"></a> Join the Team</h2>
<p>Are you a developer looking for a new challenge? We are searching for a freelancer to help us with the development of the first version of the new dashboard. This is a 2-3 month project that provides an exciting opportunity to contribute to the future of data visualization and Node-RED. If you're interested, <a href="https://boards.greenhouse.io/flowfuse/jobs/4911532004">we'd love to hear from you.</a></p>
<h2 id="charting-the-course" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/dashboard-announcement/#charting-the-course"></a> Charting the Course</h2>
<p>As we embark on this new journey, we are excited about the potential of the successor to the Node-RED Dashboard. The road ahead will be filled with challenges, but we are confident that with the help of the community, and a dedicated team, we will create a tool that surpasses its predecessor in every way.</p>
<p>Join us on this exciting journey as we innovate, create, and redefine the future of data visualization with the successor to the Node-RED Dashboard.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/Node-RED as a No-Code EtherNet/IP to S7 Protocol ConverterBeginner tutorial for using Node-RED as free industrial protocol converter2023-06-20T00:00:00Z<p>Frequently in industrial automation, there's a need for two devices that use different protocols to communicate with each other, requiring protocol conversion.<br />
In this tutorial, we present a mock scenario where Node-RED is used to enable an Allen Bradley PLC, which uses ethernet/IP, to communicate with a Siemens PLC, which uses S7, using a no-code solution. This example is geared toward beginners and assumes that the end-user knows how to use PLCs, but may be using FlowFuse or Node-RED for the first time.</p>
<!--more-->
<h2 id="premise" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#premise"></a> Premise</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-650.avif 650w, https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-650.webp 650w, https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-650.jpeg 650w, https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="FlowFuse Mock production facility" alt="Mock production facility" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-1-ye57wl7QqO-650.jpeg" width="1300" height="1077" /></picture></p>
<p>The figure above shows the layout of a mock production facility. Inside this facility, operations suggested adding stack lights as an extra visual aid for operators to get a quick status of its 4 conveyor lines, avoiding the need to constantly monitor the HMI/SCADA displays.<br />
Engineering has suggested adding a siemens S7 1200 PLC with an IO link connection to 4 stacklights, with each line PLC sending basic status information to the stacklight PLC to control the stack light outputs.<br />
Line 1-3 PLCs are Siemens-based, and can communicate with the stacklight PLC natively over S7. But line 4 is an Allen Bradley PLC that uses ethernet/IP, and can't communicate with the stacklight PLC without some form of protocol conversion.<br />
Traditionally, we'd use protocol gateway hardware, like Anybus or Red Lion, to convert ethernet/IP to S7.<br />
But for this application, we will instead use FlowFuse, a pure software-based approach, to convert ethernet/IP to S7. Let's walk through the process.</p>
<h2 id="pre-requisites-and-set-up" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#pre-requisites-and-set-up"></a> Pre-Requisites and Set Up</h2>
<h3 id="flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#flowfuse"></a> FlowFuse</h3>
<p>In addition to our two PLCs, we’ll be using FlowFuse software to serve our Node-RED instance. You can either self-host, on-premise or in the cloud. Or use the managed service <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>.</p>
<p>In this example, we will be using a self-hosted FlowFuse instance running on <a href="https://flowfuse.com/docs/install/docker/">Docker</a>.</p>
<h3 id="data-treatment-on-ethernet%2Fip-plc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#data-treatment-on-ethernet%2Fip-plc"></a> Data Treatment on Ethernet/IP PLC</h3>
<p>In our Allen Bradley line 4 PLC, we will send some arbitrary tags of various datatypes to the stacklight PLC for illustrative purposes, described in table 1 below -</p>
<table>
<thead>
<tr>
<th><strong>Tag</strong></th>
<th><strong>Data Type</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>Conveyor_RTS</td>
<td>BOOL</td>
<td>Conveyor Ready to Start</td>
</tr>
<tr>
<td>Robot_RTS</td>
<td>BOOL</td>
<td>Robot is Ready to Start</td>
</tr>
<tr>
<td>Robot_Position</td>
<td>REAL</td>
<td>Robot Arm position (degrees)</td>
</tr>
<tr>
<td>Conveyor_Running</td>
<td>BOOL</td>
<td>Conveyor is running</td>
</tr>
<tr>
<td>Line4_State</td>
<td>DINT</td>
<td>Line 4 Machine State</td>
</tr>
<tr>
<td>Line4_Fault</td>
<td>BOOL</td>
<td>Line 4 is faulted</td>
</tr>
</tbody>
</table>
<p>Table 1 - Line 4 Tags to be sent to Stacklight PLC</p>
<p>We can send any atomic data type we want, but it must be globally (controller) scoped.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-650.avif 650w, https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-650.webp 650w, https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-650.jpeg 650w, https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the AB Controller Tags" alt=""Screenshot showing the AB Controller Tags"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-2-B85UrCEqqF-650.jpeg" width="1300" height="613" /></picture></p>
<p>Each tag must also have external read/write access enabled.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-3-S-KK4bZRbG-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-3-S-KK4bZRbG-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the AB Tag Properties" alt=""Screenshot showing the AB Tag Properties"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-3-S-KK4bZRbG-650.jpeg" width="650" height="800" /></picture></p>
<h3 id="data-treatment-on-s7-plc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#data-treatment-on-s7-plc"></a> Data Treatment on S7 PLC</h3>
<p>In the Siemens PLC, we have a DB for the data from the Line 4 PLC to be written to.</p>
<ul>
<li>
<p>In the DBs attributes, “optimized block access” must be disabled.</p>
</li>
<li>
<p>The tags must be writeable and accessible</p>
</li>
<li>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-650.avif 650w, https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-650.webp 650w, https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-650.jpeg 650w, https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Siemens Tag DB Properties" alt=""Screenshot showing the Siemens Tag DB Properties"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-4-gTapN2Dmcp-650.jpeg" width="1300" height="814" /></picture></p>
<p>“No protection” must be set in the CPU properties</p>
</li>
<li>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-5-bHnio1WobD-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-5-bHnio1WobD-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Siemens CPU Properties" alt=""Screenshot showing the Siemens CPU Properties"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-5-bHnio1WobD-650.jpeg" width="650" height="730" /></picture></p>
</li>
</ul>
<h2 id="create-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#create-the-flow"></a> Create The Flow</h2>
<p>With both PLCs up and running and properly set up to send/receive remote data, we can now create a flow to act as our protocol converter.</p>
<h3 id="install-custom-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#install-custom-nodes"></a> Install Custom Nodes</h3>
<p>First, we need to add two custom nodes that will give Node-RED the ability to read/write ethernet/IP and S7 data.</p>
<p>Click the hamburger icon → manage pallette</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-6-Yso6QhXb4l-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-6-Yso6QhXb4l-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Screenshot showing the 'Manage palette option' in the menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-6-Yso6QhXb4l-650.jpeg" width="650" height="1009" /></picture></p>
<p>On the <code>install</code> tab, search for <code>s7</code> and install the <code>node-red-contrib-s7</code> node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-7-sV8cneyMtr-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-7-sV8cneyMtr-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Installing S7 node" alt=""Installing S7 node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-7-sV8cneyMtr-650.jpeg" width="650" height="663" /></picture></p>
<p>Next, search for <code>ethernet</code> and install the <code>node-red-contrib-cip-ethernet-ip</code> node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-8-pqnVSLL27Y-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-8-pqnVSLL27Y-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="InstallING EthernetIP Node" alt=""InstallING EthernetIP Node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-8-pqnVSLL27Y-650.jpeg" width="650" height="321" /></picture>
Go to the <code>nodes</code> tab and confirm both custom nodes have been properly installed.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-9-QFBNDpXbSp-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-9-QFBNDpXbSp-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of 'Nodes' tab showing Installed nodes List" alt=""Screenshot of 'Nodes' tab showing Installed nodes List"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-9-QFBNDpXbSp-650.jpeg" width="650" height="547" /></picture></p>
<h3 id="set-up-ethernet%2Fip-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#set-up-ethernet%2Fip-data"></a> Set Up Ethernet/IP Data</h3>
<p>Let’s start by dragging a <code>eth-ip in</code> node onto the pallette. Then add a new endpoint, which will point to our Line4 PLC.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-650.avif 650w, https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-650.webp 650w, https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-650.jpeg 650w, https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing dragged 'eth-ip in' node and it's config tab" alt=""Screenshot showing dragged 'eth-ip in' node and it's config tab"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-10-UT4daifFRZ-650.jpeg" width="1300" height="667" /></picture></p>
<p>In the endpoint <code>connection</code> properties, the connection information must match the PLC, so set the IP address and CPU slot number appropriately. Also, the default cycle time is 500ms. Depending on your application, polling the CPU at 500ms may be appropriate. But being that this is a simple stacklight, 500ms is unnecessarily fast. So we will change it to 1000ms, which is a more appropriate polling rate for this type of application.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-11-UlU_bU60W4-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-11-UlU_bU60W4-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the eth-ip Endpoint config" alt=""Screenshot showing the eth-ip Endpoint config"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-11-UlU_bU60W4-650.jpeg" width="650" height="365" /></picture></p>
<p>On the <code>Tags</code> tab, populate the tag information to match our Allen Bradley PLC. Then select <code>Update</code> to complete configuration of the <code>eth-ip endpoint</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-12-242Kc1telm-650.avif 650w, https://flowfuse.com/img/e-to-p-12-242Kc1telm-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-12-242Kc1telm-650.webp 650w, https://flowfuse.com/img/e-to-p-12-242Kc1telm-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-12-242Kc1telm-650.jpeg 650w, https://flowfuse.com/img/e-to-p-12-242Kc1telm-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing eth-ip Endpoint Tags" alt=""Screenshot showing eth-ip Endpoint Tags"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-12-242Kc1telm-650.jpeg" width="1300" height="468" /></picture></p>
<p>Now that we have our endpoint, let’s finish configuring the <code>eth-ip in</code> node.</p>
<ol>
<li>
<p>select the endpoint we just created</p>
</li>
<li>
<p>select the first tag in the drop-down</p>
</li>
<li>
<p>give the node a descriptive name</p>
</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-13-v5YoDA9smq-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-13-v5YoDA9smq-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screeshot showing the eth-ip in Node config" alt="Screeshot showing the eth-ip in Node config" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-13-v5YoDA9smq-650.jpeg" width="650" height="526" /></picture></p>
<p>Now let’s set up a quick test to confirm our PLC connection is valid by adding a <code>debug</code> node to the <code>eth-ip in</code> node. Then hit <code>deploy</code>.</p>
<ul>
<li>note - you can see we also have a <code>comment</code> above the nodes that describes what is happening. This is optional but good practice to help organize and understand your flow.</li>
</ul>
<p>The output of the debug console did not report any errors so communication appears to be okay.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-650.avif 650w, https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-650.webp 650w, https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-650.jpeg 650w, https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the output of eth-ip in Debug panel" alt="Screenshot showing the output of eth-ip in Debug panel" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-14-0DtND9Gl9G-650.jpeg" width="1300" height="311" /></picture></p>
<p>But just to confirm, let’s toggle the value and see if comes through.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-650.avif 650w, https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-650.webp 650w, https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-650.jpeg 650w, https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the eth-ip node output in Debug panel after Toggle" alt="Screenshot showing the eth-ip node output in Debug panel after Toggle" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-15-lpwdxRuF3e-650.jpeg" width="1300" height="153" /></picture></p>
<p>So by toggling the value and see the result, here we confirmed 2 things:</p>
<ul>
<li>
<p>We can detect changes in value</p>
</li>
<li>
<p>the <code>eth-ip in</code> node only sends a message when the value changes, also known as Report by Exception.</p>
</li>
</ul>
<p>Because the <code>eth-ip in</code> node implicitly uses report by exception, and the protocol doesn't rely on contiguous data consistency (unlike modbus, for instance), we can receive our data one tag at a time to keep our flow simple.</p>
<p>Now we can remove the debug node and add the additional <code>eth-ip in</code> nodes to receive the remaining tags from our Line 4 PLC.</p>
<p>Here’s how the the flow should look at this point.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-16-l4vLc4wh8V-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-16-l4vLc4wh8V-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of Line 4 PLC Nodes" alt="Screenshot of Line 4 PLC Nodes" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-16-l4vLc4wh8V-650.jpeg" width="650" height="1115" /></picture></p>
<h3 id="set-up-s7-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#set-up-s7-data"></a> Set Up S7 Data</h3>
<p>Now we’ll set up the S7 endpoint, using an <code>s7 out</code> node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-650.avif 650w, https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-650.webp 650w, https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-650.jpeg 650w, https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of s7 out Node on Palette" alt="Screenshot of s7 out Node on Palette" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-17-LDUD1atLR_-650.jpeg" width="1300" height="723" /></picture></p>
<p>Populate the connection properties to match your hardware. The cycle time is updated to 1000ms to match the cycle time of our <code>eth-ip in</code> nodes. You can adjust this value to match your intended application.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-18-RgthciB-S8-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-18-RgthciB-S8-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the S7 endpoint Connection" alt=""Screenshot showing the S7 endpoint Connection"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-18-RgthciB-S8-650.jpeg" width="650" height="541" /></picture></p>
<p>On the <code>Variables</code> tab, some special formatting is required to point to the absolute reference of the tag DB location in the S7 PLC.</p>
<p>For information on how to format S7 absolute tag references in a way the <code>s7 endpoint</code> node is expecting, refer to the <a href="https://flows.nodered.org/node/node-red-contrib-s7">node documentation</a> for further information.</p>
<p>For reference, here is an example of how we set the tags in our stacklight PLC example and how it looks in our <code>s7 endpoint</code>.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-650.avif 650w, https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-650.webp 650w, https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-650.jpeg 650w, https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of s7 endpoint Variables" alt=""Screenshot of s7 endpoint Variables"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-19-TFcKvI7Jm2-650.jpeg" width="1300" height="812" /></picture></p>
<p>Once the tags are populated we can select our configured endpoint from the dropdown list, point to our first variable, <code>Conveyor_RTS</code>, and give the node a name.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-20-4kgUIRd_TZ-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-20-4kgUIRd_TZ-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of S7 out Config" alt=""Screenshot of S7 out Config"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-20-4kgUIRd_TZ-650.jpeg" width="650" height="380" /></picture></p>
<p>Repeat this process for the remaining tags.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-650.avif 650w, https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-650.webp 650w, https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-650.jpeg 650w, https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of Stacklight PLC Nodes" alt=""Screenshot of Stacklight PLC Nodes"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-21-IaLyrhvyX7-650.jpeg" width="1300" height="1103" /></picture></p>
<h2 id="test-the-conversion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#test-the-conversion"></a> Test the Conversion</h2>
<p>The only thing remaining is to simply wire the nodes together, and confirm the values pass through.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-650.avif 650w, https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-650.webp 650w, https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-650.jpeg 650w, https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of the complete flow with live Data" alt=""Screenshot of the complete flow with live Data"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-22-El6qhMN6fv-650.jpeg" width="1300" height="541" /></picture></p>
<p>Manipulate the incoming values and confirm the data passes through as expected. Because of the report by exception nature of the <code>eth-ip in</code> node, tag changes should be near instantaneous on the receiving PLC.</p>
<p>We can stop here, but we can improve this flow by adding a <code>filter</code> node on our REAL data-type, <code>Robot_Position</code>.</p>
<h3 id="add-filter-to-real-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#add-filter-to-real-data"></a> Add Filter to REAL data</h3>
<p>Depending on how noisy the REAL data is, which is common with unfiltered 4-20mA field transmitters, and how much granularity you need to capture, it is good practice to add a filter on REAL data to reduce FieldBus traffic coming out of our soft protocol converter.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--650.avif 650w, https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--650.webp 650w, https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--650.jpeg 650w, https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the Filter node Configuration" alt=""Screenshot showing the Filter node Configuration"" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-23-okXk3CiZ2--650.jpeg" width="1300" height="566" /></picture></p>
<p>In the example above, we arbitrarily applied a 3% <a href="https://flowfuse.com/node-red/core-nodes/filter/">deadband</a>
to the <code>Robot_Position</code> value, which means that the value must change by greater than or equal to 3% compared to the last input value, or else the data will be discarded before being sent to the stacklight PLC.</p>
<p>You can adjust the deadband to find the right balance for your particular application.</p>
<p>We can see the effect the deadband filter had by adding debug nodes before and after the filter.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-650.avif 650w, https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-650.webp 650w, https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-650.jpeg 650w, https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Filter Node Debug" alt="Filter Node Debug" loading="lazy" decoding="async" src="https://flowfuse.com/img/e-to-p-24-FmsWDsCm9d-650.jpeg" width="1300" height="285" /></picture></p>
<p>As shown above, when <code>Robot_Position</code> changed from 15.6 to 15.6999..., the value was captured on the input of the filter, but was discarded on the output.</p>
<p>When the <code>Robot_Position</code> went from 15.6999 to 18, the filter allowed it to pass as it exceeded the deadband limit we had set.</p>
<p>Use filters to optimize your fieldbus converter network performance, especially if dealing with noisy signals or large quantities of REAL datatypes.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/node-red-as-a-no-code-ethernet_ip-to-s7-protocol-converter/#conclusion"></a> Conclusion</h2>
<p>In this tutorial, we demonstrated how to use Node-RED as a free Ethernet/IP to S7 protocol converter using a simple no-code approach. We showed how to configure PLC tags to be sent remotely using Ethernet/IP, how to configure PLC tags to be received remotely using S7, and how to create the flow to use Node-RED to seamlessly convert incoming PLC data between the two protocols using <code>node-red-contrib-cip-ethernet-ip</code> and <code>node-red-contrib-s7</code> custom nodes. We also took things one step further and added a <code>filter</code> node to optimize FieldBus network traffic by putting a deadband on REAL data being sent to the receiving PLC.</p>
<p>The end result is a simple to set up, free and performant industrial protocol converter that requires minimal PLC configuration, which allows this application to be applied in non-mission critical production systems with minimal, if any downtime. Additionally, the protocol traffic can be visually observed in real-time for easy trouble-shooting and fault analysis by simply accessing the Node-RED UI.</p>
<p>In later tutorials, we can show ways this simple flow can be extended to add additional capabilities not normally available in traditional off-the-shelf protocol gateways. If you found this tutorial helpful, or have any questions or comments, please leave us a comment and let us know your thoughts.</p>
<p>JSON source code for the flow used in this tutorial is provided below -</p>
<div id="nr-flow-123" style="height: 500px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow123 = "\n[{\"id\":\"ad7b17411c8e83aa\",\"type\":\"tab\",\"label\":\"Line 4 to Stacklight PLC\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"c97a4c9bd1981757\",\"type\":\"comment\",\"z\":\"ad7b17411c8e83aa\",\"name\":\"AB EIP/CIP - Line 4 PLC\",\"info\":\"\",\"x\":190,\"y\":140,\"wires\":[]},{\"id\":\"2cc5227ef6a90814\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Conveyor_RTS\",\"program\":\"\",\"name\":\"Read Conveyor_RTS\",\"x\":200,\"y\":200,\"wires\":[[\"fe18ef80f9e18c13\"]]},{\"id\":\"9308dcbda17274c7\",\"type\":\"comment\",\"z\":\"ad7b17411c8e83aa\",\"name\":\"Siemens S7 - Stacklight PLC\",\"info\":\"\",\"x\":620,\"y\":140,\"wires\":[]},{\"id\":\"fe18ef80f9e18c13\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Conveyor_RTS\",\"name\":\"Write Conveyor_RTS\",\"x\":620,\"y\":200,\"wires\":[]},{\"id\":\"94fe6b73efa1c56b\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Robot_RTS\",\"program\":\"\",\"name\":\"Read Robot_RTS\",\"x\":180,\"y\":280,\"wires\":[[\"7774d6ce188c288c\"]]},{\"id\":\"7e9564cd59e3d0a2\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Robot_Position\",\"program\":\"\",\"name\":\"Read Robot_Position\",\"x\":200,\"y\":360,\"wires\":[[\"832807bfdc4b76f0\"]]},{\"id\":\"c0f712b9e355f1f8\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Conveyor_Running\",\"program\":\"\",\"name\":\"Read Conveyor_Running\",\"x\":210,\"y\":440,\"wires\":[[\"fbf1b3e38897a9c7\"]]},{\"id\":\"db77621e418f1222\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Line4_State\",\"program\":\"\",\"name\":\"Read Line4_State\",\"x\":190,\"y\":520,\"wires\":[[\"cdeffd9e52cc4384\"]]},{\"id\":\"848af9b76f969dd2\",\"type\":\"eth-ip in\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"4ab2910b66e16220\",\"mode\":\"single\",\"variable\":\"Line4_Fault\",\"program\":\"\",\"name\":\"Read Line4_Fault\",\"x\":190,\"y\":600,\"wires\":[[\"0c595b0ac2550593\"]]},{\"id\":\"7774d6ce188c288c\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Robot_RTS\",\"name\":\"Write Robot_RTS\",\"x\":610,\"y\":280,\"wires\":[]},{\"id\":\"f1572463c50bb4cb\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Robot_Position\",\"name\":\"Write Robot_Position\",\"x\":620,\"y\":360,\"wires\":[]},{\"id\":\"fbf1b3e38897a9c7\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Conveyor_Running\",\"name\":\"Write Conveyor_Running\",\"x\":630,\"y\":440,\"wires\":[]},{\"id\":\"cdeffd9e52cc4384\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Line4_State\",\"name\":\"Write Line4_State\",\"x\":610,\"y\":520,\"wires\":[]},{\"id\":\"0c595b0ac2550593\",\"type\":\"s7 out\",\"z\":\"ad7b17411c8e83aa\",\"endpoint\":\"a1bec25858c6f3ef\",\"variable\":\"Line4_Fault\",\"name\":\"Write Line4_Fault\",\"x\":610,\"y\":600,\"wires\":[]},{\"id\":\"832807bfdc4b76f0\",\"type\":\"rbe\",\"z\":\"ad7b17411c8e83aa\",\"name\":\"\",\"func\":\"deadbandEq\",\"gap\":\"3%\",\"start\":\"\",\"inout\":\"in\",\"septopics\":true,\"property\":\"payload\",\"topi\":\"topic\",\"x\":420,\"y\":360,\"wires\":[[\"f1572463c50bb4cb\"]]},{\"id\":\"4ab2910b66e16220\",\"type\":\"eth-ip endpoint\",\"address\":\"192.168.0.5\",\"slot\":\"0\",\"cycletime\":\"1000\",\"name\":\"Line4\",\"vartable\":{\"\":{\"Conveyor_RTS\":{\"type\":\"BOOL\"},\"Robot_RTS\":{\"type\":\"BOOL\"},\"Robot_Position\":{\"type\":\"REAL\"},\"Conveyor_Running\":{\"type\":\"BOOL\"},\"Line4_State\":{\"type\":\"DINT\"},\"Line4_Fault\":{\"type\":\"BOOL\"}}}},{\"id\":\"a1bec25858c6f3ef\",\"type\":\"s7 endpoint\",\"transport\":\"iso-on-tcp\",\"address\":\"192.168.0.10\",\"port\":\"102\",\"rack\":\"0\",\"slot\":\"1\",\"localtsaphi\":\"01\",\"localtsaplo\":\"00\",\"remotetsaphi\":\"01\",\"remotetsaplo\":\"00\",\"connmode\":\"rack-slot\",\"adapter\":\"\",\"busaddr\":\"2\",\"cycletime\":\"1000\",\"timeout\":\"3000\",\"name\":\"Stacklight PLC\",\"vartable\":[{\"addr\":\"DB1,X0.0\",\"name\":\"Conveyor_RTS\"},{\"addr\":\"DB1,X0.1\",\"name\":\"Robot_RTS\"},{\"addr\":\"DB1,R2\",\"name\":\"Robot_Position\"},{\"addr\":\"DB1,X6.0\",\"name\":\"Conveyor_Running\"},{\"addr\":\"DB1,DI8\",\"name\":\"Line4_State\"},{\"addr\":\"DB1,X12.0\",\"name\":\"Line4_Fault\"}]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow123.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-123') })</script>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/introducing-the-flowforge-community-forum/Introducing the FlowFuse Community ForumA Community Forum for support, inspiration, and knowledge sharing2023-06-12T12:00:00ZZJ van de Weg<p>We are thrilled to announce the launch of the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">Community Forum for FlowFuse</a>.
A forum dedicated to empowering developers and enthusiasts to create innovative
applications using FlowFuse, Node-RED and related technologies.</p>
<!--more-->
<p>Our community faces a new set of challenges, for example how to configure FlowFuse templates as administrator, or integration of the FlowFuse platform into another existing environment. These questions do not fit on the Node-RED discourse which is why FlowFuse now sports our own Community Forum. Furthermore, it’s the intent to keep the Node-RED forums vendor agnostic by the OpenJS foundation. Given FlowFuse is a vendor, it’s a fine balance to find. We hope and intend to be additive to the Node-RED community at large.</p>
<p>At FlowFuse, our vision is to create a thriving community of Node-RED and MQTT enthusiasts who are passionate about building real-world solutions. We believe in the power of collaboration, knowledge sharing, and problem-solving. With this in mind, FlowFuse aims to foster a positive and inclusive environment where members can engage in discussions, exchange ideas, and support one another.</p>
<p>Initially the forum is used to allow for discussions under each blog post on the FlowFuse blog, as well as a venue to provide community support. We also welcome discussions on the roadmap and backlog of FlowFuse. We have hopes that this community thrives and provides guidance, inspiration and opportunity to learn.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/Community News June 2023Your monthly update for the FlowFuse and Node-RED communities2023-06-09T00:00:00Z<p>Welcome to the FlowFuse newsletter for June 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="new-release" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#new-release"></a> New Release</h2>
<p>This week we released FlowFuse 1.8, featuring high availability for Node-RED and DevOps software delivery pipelines. Both these features were in high demand from our community and will make it easier to reliably deliver Node-RED for business critical applications.</p>
<p>Read about the details of FlowFuse 1.8 in our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/">release announcement</a>.</p>
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#upcoming-events"></a> Upcoming events</h2>
<h3 id="building-node-red-applications-for-scalability-and-high-availability" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#building-node-red-applications-for-scalability-and-high-availability"></a> Building Node-RED Applications for Scalability and High Availability</h3>
<p>Our June webinar will focus on the new FlowFuse 1.8 feature of running high availability Node-RED applications. Marian Demme, FlowFuse Product Manager, will lead this session and share practical insights and best practices to show how FlowFuse can unlock the true potential of Node-RED in large-scale deployments.</p>
<p><a href="https://flowfuse.com/webinars/2023/building-scalable-ha-node-red/">Sign-up today</a> to join us on June 22.</p>
<h3 id="build-an-edge-to-cloud-solution-with-the-ming-stack" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#build-an-edge-to-cloud-solution-with-the-ming-stack"></a> Build an Edge-to-Cloud Solution with the MING Stack</h3>
<p>On June 27, FlowFuse is doing a webinar with our friends at InfluxDB. A great opportunity to see how easy it is to use Node-RED and InfluxDB to send data from the edge to the cloud. <a href="https://www.influxdata.com/resources/build-an-edge-to-cloud-solution-with-the-ming-stack/?utm_source=partner&utm_medium=referral&utm_campaign=2023-06-27_Webinar_FlowFuse-NodeRED&utm_term=speaker">Sign-up today</a>.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#from-our-blog"></a> From our Blog</h2>
<ul>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/">Bringing High Availability to Node-RED</a> - FlowFuse CTO discusses the strategy for delivering high availability in the FlowFuse platform.</p>
</li>
<li>
<p>Two articles featuring how to connect Modbus data with Node-RED:</p>
<ul>
<li><a href="https://flowfuse.com/node-red/protocol/modbus/">Using Node-RED to Visualize Industrial Production Data via Modbus</a></li>
<li>[Best Practices Integrating a Modbus Device With Node-RED](/blog/2023/05/integrating modbus with node-red/)</li>
</ul>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/3-quick-node-red-tips-7/">Node-RED Tips - Dashboard Edition</a> - A new set of Node-RED quick tips that are focused on using Node-RED Dashboard.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/">Persisting chart data in Node-RED Dashboards</a> - How to store data from the Node-RED Dasbhaord chart node.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/node-red-community-survey-results/">Node-RED Community Survey Results</a> - A quick summary of the Node-RED Community Survey results.</p>
</li>
<li>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/">FlowFuse 1.7 Now Available with Remote Node-RED Editor Access</a></p>
</li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#from-the-community"></a> From the Community</h2>
<p>Gerrit Riessen has published a list of <a href="https://gorenje.medium.com/fourteen-for-fourteen-against-why-i-love-hate-and-connect-with-node-23797f9466ec">Pros and Cons for using Node-RED</a>. It is a pretty comprehensive list so check it out. FlowFuse is working to address some of the cons in Gerrit's list, specifically software delivery pipelines and the ability to deploy out to many end points.</p>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/community-news-06/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li>
<p><a href="https://boards.greenhouse.io/flowfuse/jobs/4796271004">DevOps Engineer</a></p>
</li>
<li>
<p><a href="https://boards.greenhouse.io/flowfuse/jobs/4843566004">Sales Representative</a></p>
</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/FlowFuse now offers High Availability Node-REDFlowFuse 1.8 makes Node-RED applications more reliable and scalable, plus more streamline deployment pipelines.2023-06-08T00:00:00Z<p>FlowFuse 1.8 introduces two key features that allow organizations to reliably deploy Node-RED applications into production. In 1.8, it is now possible to run Node-RED applications with high availability so the application is more scalable and more fault tolerant. FlowFuse 1.8 also introduces software deliver pipelines, so development teams can now set up dev/test/production environments for their Node-RED applications.</p>
<!--more-->
<h2 id="more-reliable-and-scalable-node-red-applications" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#more-reliable-and-scalable-node-red-applications"></a> More reliable and scalable Node-RED applications</h2>
<p>FlowFuse now makes it possible to deploy business critical applications built in Node-RED that are reliable and scalable. The new 1.8 features allows a Node-RED instance to be deployed in high availability mode, meaning two instances of the same Node-RED flows are available behind a load balancer. This allows for increased traffic to be automatically distributed across the two Node-RED instances. This means your Node-RED applications can handle more traffic and experience less downtime. For more details, please see our <a href="https://flowfuse.com/docs/user/high-availability/">documentation</a>.</p>
<p>Additionally, we're pleased to offer a 30-day premium trial license for self-managed installs on Kubernetes. To avail of this offer, book a demo at <a href="https://flowfuse.com/book-demo/">flowforge.com/book-demo</a>.</p>
<p>High Availability is our first <a href="https://flowfuse.com/handbook/product/versioning/#preview-features">preview feature</a>, and your feedback is crucial. We encourage you to try out HA in your Node-RED instances and share your experiences with us. Your feedback will help us refine this feature and make it even better.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/mbDkjKhVwIw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="devops-pipelines" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#devops-pipelines"></a> DevOps Pipelines</h2>
<p>FlowFuse 1.8 introduces the concept of Pipelines to better organize your Node-RED development. Development team can now set up different staging environments for different steps in the development cycle, ex. test, development and production. Node-RED instances can be pushed along a pipeline as they move along the development process. This allows for a better organized and predictable development process for your team.</p>
<p>The new Pipelines feature builds upon the Staged Development support that we introduced in<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/">FlowFuse Version 1.4</a>. We highly recommend that development teams avoid developing their flows directly in production instances. This approach fosters a more reliable and robust development process, reducing the risks associated with production environment modifications. Instead, start your development in a dedicated development or test instance and then deploy your Node-RED instance to production once they have been thoroughly tested and reviewed.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/Pbql22f3vqY" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="user-interface-for-device-agent" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#user-interface-for-device-agent"></a> User interface for Device Agent</h2>
<p>In our previous release, we introduced <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/">Editor Access for Devices</a>. Now, the FlowFuse Device Agent now comes with its very own User Interface (UI) for configuration.</p>
<p>Imagine this: Your industrial equipment arrives with the Device Agent preinstalled. In the past, you might have faced challenges in configuring and connecting your device with FlowFuse, particularly if you had no direct shell access. But not any more.</p>
<p>With the newly introduced UI, you can easily set up and connect your device with FlowFuse without needing to access the command line interface directly. This simplifies the process significantly and saves you time. For more details, see our <a href="https://flowfuse.com/docs/device-agent/introduction/">documentation</a>.</p>
<h2 id="node-red-3.1-beta-3-available" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#node-red-3.1-beta-3-available"></a> Node-RED 3.1 Beta 3 Available</h2>
<p>FlowFuse Cloud is a great place to try out the new Node-RED features, with FlowFuse Cloud now including the <a href="https://discourse.nodered.org/t/node-red-3-1-0-beta-3-released/78716">Node-RED 3.1.0-beta.3</a>. If you want to try this version you can <a href="https://flowfuse.com/docs/user/instance-settings/">duplicate your instance</a> and <a href="https://flowfuse.com/docs/user/changestack/">upgrade your stack</a>.</p>
<h2 id="ongoing-topics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#ongoing-topics"></a> Ongoing Topics</h2>
<h3 id="soc2-certification" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#soc2-certification"></a> SOC2 Certification</h3>
<p>We're making great strides on our journey towards SOC2 certification, striving to meet the highest industry standards for security and privacy. While we're not quite ready to disclose specific milestones, be assured that everything is progressing smoothly. As we continue working diligently towards our target, we promise to keep you informed every step of the way. Our unwavering commitment to deliver secure and private services to our customers and partners remains our foremost priority. Stay tuned for more updates!</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our <a href="https://flowfuse.com/changelog/">Product Roadmap Page</a> to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#bug-fixes"></a> Bug Fixes</h2>
<p>When editing a device in developer mode via the tunnel/proxy connection, a list of team projects are not presented in the "Target" field of the project-link nodes. <a href="https://github.com/FlowFuse/flowfuse/issues/2228">#2228</a></p>
<p>If a user invites an external user to their team with an sso-enable email domain, when that user registers and logs in, they are not added to the team they were invited to and must be re-invited. <a href="https://github.com/FlowFuse/flowfuse/issues/2232">#2232</a></p>
<p>No warning given if tying to start device editor when NR is not running <a href="https://github.com/FlowFuse/flowfuse/issues/2233">#2233</a></p>
<p>Stuck on the form Create a new Application & Instance after using an already known instance name <a href="https://github.com/FlowFuse/flowfuse/issues/2221">#2221</a></p>
<p>If the device agent finds itself in Developer mode, it stops pulling snapshots from the platform <a href="https://github.com/FlowFuse/device-agent/issues/97">#97</a></p>
<p>Accessing the Admin Settings General page resets the Platform Statstics token <a href="https://github.com/FlowFuse/flowfuse/issues/2140">#2140</a></p>
<p>Selection of Team and Instance in nr-tools-plugin not possible <a href="https://github.com/FlowFuse/nr-tools-plugin/issues/15">#15</a></p>
<p>HOME env var not set within Node-RED process <a href="https://github.com/FlowFuse/flowforge-nr-launcher/issues/117">#117</a></p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.8.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/flowforge-1-8-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there. Additionally you can go the the <a href="https://discourse.nodered.org/c/vendors/flowfuse/24">community forum</a> if you have
any feedback or feature requests.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/Use any npm module in Node-REDSee how you can easily import any npm module, for use in a Node-RED function node.2023-06-05T00:00:00ZJoe PavittSteve McLaughlin<p>Node-RED has <a href="https://flows.nodered.org/search?type=node" target="_blank">an incredibly rich resource of integrations available</a>, but sometimes you need that little bit of extra functionality, or access to a Node.js module that doesn't have it's own custom nodes in Node-RED. <strong>We can easily import any npm module within the built-in Node-RED function nodes.</strong></p>
<!--more-->
<p>Historically in Node-RED, you would have needed to manually <code>npm install</code> modules from the command line, but now that it's so easy to run Node-RED in the Cloud, where you don't have easy access to those tools, what are the other options available?</p>
<h2 id="function-node---setup" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/#function-node---setup"></a> Function Node - Setup</h2>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/npmimport-add--dD1VwKW6h-650.avif 650w, https://flowfuse.com/img/npmimport-add--dD1VwKW6h-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/npmimport-add--dD1VwKW6h-650.webp 650w, https://flowfuse.com/img/npmimport-add--dD1VwKW6h-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/npmimport-add--dD1VwKW6h-650.jpeg 650w, https://flowfuse.com/img/npmimport-add--dD1VwKW6h-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Location of the 'add' button in order to import an npm module intoa function node" alt="Location of the "add" button in order to import an npm module intoa function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/npmimport-add--dD1VwKW6h-650.jpeg" width="1300" height="753" /></picture></p>
<p>All you need is the name of the module you want to import, then:</p>
<ol>
<li>Drop in a new "function" node & double-click it</li>
<li>Switch to the "Setup" tab</li>
<li>Underneath the "modules" tab, click "+ add" in the bottom-left of the window.</li>
<li>Enter the name of the module you want to use in the newly created row, and (optionally) modify the <code>variable</code> that this module will be imported in as.</li>
<li>Switch back to the "On Message" tab and write your function. Your new module will be available via the <code>variable</code> you defined in the "Setup" tab.</li>
</ol>
<h2 id="example%3A-moment.js" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/#example%3A-moment.js"></a> Example: Moment.js</h2>
<video width="560" height="315" controls="">
<source src="https://website-data.s3.eu-west-1.amazonaws.com/MomentJS+Demo.mp4" type="video/mp4" />
</video>
<p>Recently we wanted to use <a href="https://www.npmjs.com/package/moment">moment</a> for some custom date calculations. Whilst there was set of <a href="https://flows.nodered.org/node/node-red-contrib-moment">Moment Node-RED nodes</a> already available, it didn't have all of the functionality we needed.</p>
<p>So, all we needed to do was import the module into a function node, and define our comparison there instead, here's a working example:</p>
<h2 id="example%3A-easy-crc" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/#example%3A-easy-crc"></a> Example: Easy CRC</h2>
<video width="560" height="315" controls="">
<source src="https://website-data.s3.eu-west-1.amazonaws.com/Easy+CRC+Demo.mp4" type="video/mp4" />
</video>
<p>Something we see <a href="https://discourse.nodered.org/search?q=crc%20order%3Alatest">a lot on the Node-RED Forums</a> are questions on how to conduct CRC calculations. There is a popular node module <code>easy-crc</code> that can be imported and used in the function nodes, e.g:</p>
<h2 id="example%3A-posthog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/#example%3A-posthog"></a> Example: PostHog</h2>
<video width="560" height="315" controls="">
<source src="https://website-data.s3.eu-west-1.amazonaws.com/PostHog+Node+Demo.mp4" type="video/mp4" />
</video>
<p>Node-RED is great for <a href="https://flowfuse.com/solutions/data-integration/">data integration</a>. We use <a href="https://posthog.com/" target="_blank">PostHog</a> for our internal Product Analysis. We record live events as they occur on FlowFuse Cloud to better understand features that are (and are not) used.</p>
<p>We wanted to investigate whether or not we could add backdated data, which in theory was possible via their <a href="https://posthog.com/docs/libraries/node" target="_blank">posthog-node</a> module. We wanted to populate it with data driven from our own database and API.</p>
<p>Within two minutes, we could wire up a node to retrieve data from our API, and then ingest it into <code>posthog-node</code> via the import of a function node.</p>
<h2 id="simplify-function-node-creation-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/import-modules/#simplify-function-node-creation-with-flowfuse"></a> Simplify Function Node Creation with FlowFuse</h2>
<p><a href="https://flowfuse.com/">FlowFuse</a> provides a powerful platform to enhance, scale, and secure your Node-RED applications efficiently. One of our latest features, the <strong>FlowFuse Assistant</strong>, is designed to streamline the process of creating Function nodes.</p>
<p>With the FlowFuse Assistant, you can leverage AI to generate Function nodes effortlessly. Just input your prompt, and the Assistant will handle the creation for you, saving time and reducing manual coding.</p>
<p>To explore how to make the most of the FlowFuse Assistant and its capabilities, check out the <a href="https://flowfuse.com/docs/user/assistant/">Assistants Documentation</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/Bringing High Availability to Node-REDHow we are tackling the hard problems of HA in FlowFuse2023-06-02T12:00:00ZNick O'Leary<p>Many companies look to deploy Node-RED into use cases that require the application
to have a high degree of availability, reliability, and scalability. Following up
our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/">previous post on the subject</a>, in this
post I’m going to look at some of the technical details of achieving HA, the
approaches available and what that means for the work we’re doing at FlowFuse
and upstream in Node-RED.</p>
<!--more-->
<p>Everyone we speak to has a different set of requirements for this topic. To help
with the discussion, I’m going to look at two ways of approaching it:</p>
<ul>
<li>The <strong>hot-spare approach</strong> where you have a second instance of the application
ready to take over when the primary fails. This achieves availability but
doesn’t contribute to scalability.</li>
<li>The <strong>load-balanced approach</strong> where you have a second active instance of the
application and work is shared between them. If either fails, the other
continues running. A side-effect of this approach is a higher potential
through-put and scalability; although in practice you need to ensure capacity
to tolerate an instance failing.</li>
</ul>
<p>To consider which approach is most appropriate in the context of Node-RED, we
need to look at the benefits and complications of each approach. It comes down
to two factors; statefulness and how work is routed.</p>
<h3 id="statefulness" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/#statefulness"></a> Statefulness</h3>
<p>There are two types of state to consider when thinking about a Node-RED flow:
<strong>explicit</strong> and <strong>implicit</strong> state.</p>
<p>Explicit state is what is programmed into the flow. For example, a flow may store
state in Context or use an external database service. Within FlowFuse we provide
two types of context - the default in-memory context store and a database-backed
persistent store. Currently the database-backed store includes a memory-caching
layer to provide better performance and interoperability. That gets tricky when
you want to have multiple instances sharing the same store. The context API
doesn’t provide a way to atomically update values - so you can get into classic
concurrency issues around two applications trying to update the same value.</p>
<p>The other type of state is that which is implicitly maintained in a flow - even
if the user hasn’t explicitly configured it. For example, the Smooth node can be
used to calculate a running average value of messages passing through it. The
node does that by keeping in memory the recent values so it can recalculate the
average with each update. If you have multiple instances, then the node will be
calculating the average for just the message its instances sees.</p>
<p>Another example of implicit state is the Batch node that can be used to group
messages into batches. Again - it will only be able to do that for the selection
of messages the instance receives.</p>
<p>It very much depends on the requirements of a flow and what nodes it uses, as to
how the state can be handled.</p>
<p>In the hot-spare approach, as only one instance is active at any time, a lot of
the explicit state handling will work as expected. However the implicit state
remains bound to the individual Node-RED instances.</p>
<p>In the load-balanced approach, care has to be taken to ensure any state generated
by the flow is done in a way that copes with multiple instances accessing it at
the same time.</p>
<p>A key take-away from this being that a flow has to be created with HA and/or scaling
in mind.</p>
<h3 id="routing-work" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/#routing-work"></a> Routing work</h3>
<p>Node-RED makes it easy to integrate with lots of different sources of events.
A couple of the most common being HTTP and MQTT. When considering how to handle
multiple instances of an application we need to think about how work is routed
to those instances.</p>
<p>HTTP is the most well understood; you put a load-balancing proxy in front of the
Node-RED instances and it takes care of sharing out the incoming requests. In
the hot-spare scenario, the proxy needs to know which instance is active - that
requires some coordination within the platform to track that properly.</p>
<p>MQTT is commonly used with Node-RED, but unlike HTTP which is in-bound, MQTT
works by having Node-RED create an out-bound connection to a broker and then
subscribing to the topics of interest. In the early days of MQTT that would mean
each instance would subscribe to the same set of topics and receive every message.
That doesn’t really fit any HA model.</p>
<p>With the publication of MQTTv5, the concept of Shared Subscriptions was added;
the ability for a group of clients to connect, subscribe to the same topic and
have the broker distribute messages between them. At this point you do get load
balancing across your Node-RED instances - as long as the MQTT nodes are suitably
configured.</p>
<p>There are lots of other nodes that can be used to trigger flows, whether by
listening for events on an API, connecting to locally attached hardware and many
things in between. Typically, those that are more cloud-aligned, such as messaging
systems like Kafka and AMQP will have very well established ways of doing load
balancing.</p>
<p>Managing out-bound connections gets more complicated in the hot-spare scenario.</p>
<p>If we only had to deal with in-bound connections, the hot-spare instance can just
sit there waiting for work to be passed its way. But once you have out-bound
connections, then you have a problem. The hot-spare instance should only create
its out-bound connections when it becomes the active instance. In real terms,
that means the Node-RED flows should only be started when the instance becomes
active.</p>
<p>With our goal to minimize the Mean Time To Recovery (MTTR), we need to find a
way to get that spare instance running as quickly as possible; if it takes just
as long to start the spare instance as it does to restart the failed primary
instance, then it isn’t much of an improvement.</p>
<p>The key here is that Node-RED allows you to start the runtime without the flows
running. That gets everything loaded and the runtime ready ahead of time. It can
then start the flows at a moment's notice with a simple call to the runtime admin
API.</p>
<h3 id="detecting-failure" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/#detecting-failure"></a> Detecting failure</h3>
<p>A key requirement of the hot-spare approach to HA is knowing when to failover to
the spare.</p>
<p>This requires close monitoring of the active instance to know whether it's still
working. How quickly you can detect failure is key to reducing the time to recovery.
This is where you have to think about the different ways an instance could fail -
has it crashed, has it hung, has it got ‘stuck’?</p>
<p>Detecting failure usually involves some combination of heartbeat ‘pings’ between
the instances to check each is able to respond to requests. The spare instance
then needs to be able to decide for itself whether it should become the active
instance - and do so safely. You do not want to accidentally have two instances
active at the same time. This can get quite complicated to achieve safely, but
there are a number of approaches that can be used. We’ll be exploring them as we
continue our journey towards HA.</p>
<h3 id="editing-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/#editing-flows"></a> Editing Flows</h3>
<p>Within the Node-RED architecture, each instance also serves up its own editor.
This is what you get when you point your web browser at it.</p>
<p>In a HA world, once you have multiple instances running behind an HTTP load
balancer, there is a tricky question of how you edit the flows. If each request
hits a different instance, just loading the editor will result in different bits
coming from different instances. That can typically be solved at the load balancer
level by creating sticky-sessions; ensuring for a given client, each request is
routed to a consistent instance. That solves part of the issue, but the next
challenge is what to do when the Deploy button is pressed. That is how new flows
are passed from the editor to the runtime. When you have multiple instances, we
need to make sure that they all get updated. That is quite a tricky problem to
solve with the current Node-RED APIs - and something we’ll be working on both in
FlowFuse and in the upstream Node-RED project to resolve.</p>
<p>That said, a more immediate solution could well be to take advantage of separate
development/production instances. You develop in a single instance and, when happy
with what you’ve got, roll it out to your HA-ready production instance. This
bypasses the need to edit the flows in the HA environment at all.</p>
<p>Whichever method is used, there is a question of how you minimize downtime whilst
deploying an update. In a purely in-bound environment, solutions can be built
where the new application is deployed alongside the old version and, when everything
is ready, the in-bound events are redirected to the new version. But that isn’t
feasible when you have out-bound connections to deal with as well. For some users,
having a scheduled maintenance window for doing updates will be completely acceptable.</p>
<p>As with the hot-spare approach to failover, a similar method could be used that
starts new instances of Node-RED alongside the old, but with the flows all stopped.
Then, once everything is ready, the old instances are stopped and the new instances
started - minimizing the downtime, although not completely removing it.</p>
<h3 id="continuing-the-ha-journey-at-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/bringing-high-availability-to-node-red/#continuing-the-ha-journey-at-flowfuse"></a> Continuing the HA journey at FlowFuse</h3>
<p>So the question is how are we going to apply all of this to what we’re building
at FlowFuse. We cannot do everything at once, so we have to prioritize which
scenarios we’re going to address first. Consequently, drawing from customer
feedback, we have chosen to start with the scaling side of high availability -
allowing multiple copies of an instance to be run with appropriate load
balancing put in front of it.</p>
<p>We are building FlowFuse as an open platform with the ability to run on top of
Docker Compose and Kubernetes. As we get into some of these HA features, we will
need to look carefully at where we can lean on these underlying technologies -
we don’t want to reinvent the wheel here.</p>
<p>Our initial focus is going to be when running in a Kubernetes environment - just
as we do with our hosted FlowFuse Cloud platform. Kubernetes provides lots of
the building blocks for creating a scalable and highly available solution, but
it certainly doesn’t do all of the work for you.</p>
<p>We've identified our initial set of tasks and changes to how we'll run Node-RED
instance with the k8s environment. You can follow our progress with this
<a href="https://github.com/FlowFuse/flowfuse/issues/2156">issue</a> on our backlog.</p>
<p>I hope this post has given some useful insight into the problems we’re looking
to solve at FlowFuse. As it's such an important requirement for many users we’ll
keep you updated as we make progress.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/3-quick-node-red-tips-7/Node-RED Tips - Dashboard EditionSave yourself time when working with Node-RED Dashboards with these three tips.2023-06-01T12:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>In this Node-RED Tips article, we are going to focus on <a href="https://flows.nodered.org/node/node-red-dashboard">Node-RED Dashboard</a>. 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.</p>
<h3 id="1.-responsive-layouts-(almost)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/3-quick-node-red-tips-7/#1.-responsive-layouts-(almost)"></a> 1. Responsive layouts (almost)</h3>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>The image below shows what happens when you change the screen size for this dashboard.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/responsive-E0mhVCUWw3-600.gif 600w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Changing the aspect ratio of the screen" alt="Changing the aspect ratio of the screen" loading="lazy" decoding="async" src="https://flowfuse.com/img/responsive-E0mhVCUWw3-600.webp" width="600" height="437" /></picture></p>
<p>If you'd like to try this out on your own Node-RED, you can import the flow below.</p>
<div id="nr-flow-117" style="height: 500px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow117 = "\n[{\"id\":\"e351b1251dfbc2f7\",\"type\":\"tab\",\"label\":\"Flow 1\",\"disabled\":false,\"info\":\"\",\"env\":[]},{\"id\":\"d388b48fcbed93a1\",\"type\":\"ui_gauge\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"group\":\"ba1ff527abfa5261\",\"order\":2,\"width\":0,\"height\":0,\"gtype\":\"gage\",\"title\":\"gauge\",\"label\":\"units\",\"format\":\"\",\"min\":0,\"max\":\"100\",\"colors\":[\"#00b500\",\"#e6e600\",\"#ca3838\"],\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":80,\"wires\":[]},{\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":120,\"wires\":[[]]},{\"id\":\"1482bcf69325aa92\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":[],\"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\":\"\",\"min\":0,\"max\":\"100\",\"colors\":[\"#00b500\",\"#e6e600\",\"#ca3838\"],\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":240,\"wires\":[]},{\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":280,\"wires\":[[]]},{\"id\":\"60a40faa335f943e\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":[],\"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\":\"\",\"min\":0,\"max\":\"100\",\"colors\":[\"#00b500\",\"#e6e600\",\"#ca3838\"],\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":380,\"wires\":[]},{\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":420,\"wires\":[[]]},{\"id\":\"bb27ef41380ec1d2\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":[],\"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\":\"\",\"min\":0,\"max\":\"100\",\"colors\":[\"#00b500\",\"#e6e600\",\"#ca3838\"],\"seg1\":\"\",\"seg2\":\"\",\"diff\":false,\"className\":\"\",\"x\":350,\"y\":500,\"wires\":[]},{\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":350,\"y\":540,\"wires\":[[]]},{\"id\":\"796f79538c15dd66\",\"type\":\"inject\",\"z\":\"e351b1251dfbc2f7\",\"name\":\"\",\"props\":[],\"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}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow117.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-117') })</script>
<h3 id="2.-add-more-than-one-series-of-data-to-a-line-chart" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/3-quick-node-red-tips-7/#2.-add-more-than-one-series-of-data-to-a-line-chart"></a> 2. Add more than one series of data to a line chart</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/temp-graph-Snc3Eb9LW2-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/temp-graph-Snc3Eb9LW2-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Graphing two series on the same line chart" alt="Graphing two series on the same line chart" loading="lazy" decoding="async" src="https://flowfuse.com/img/temp-graph-Snc3Eb9LW2-650.jpeg" width="650" height="262" /></picture></p>
<p>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.</p>
<p>If you'd like to view this chart on your own Node-RED, you can import the flow below.</p>
<div id="nr-flow-118" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow118 = "\n[{\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"outputs\":1,\"useDifferentColor\":false,\"className\":\"\",\"x\":280,\"y\":180,\"wires\":[[]]},{\"id\":\"c4a13220356f76d3\",\"type\":\"inject\",\"z\":\"58569b35dacd54f3\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[{\\\"series\\\":[\\\"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}],[{\\\"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\\\":[\\\"\\\"]}]\",\"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}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow118.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-118') })</script>
<h3 id="3.-using-sliders-and-persisting-the-current-value" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/06/3-quick-node-red-tips-7/#3.-using-sliders-and-persisting-the-current-value"></a> 3. Using sliders and persisting the current value</h3>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/slider-ui-g-ru3pSeKO-540.gif 540w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="An example of a slider in a HMI" alt="An example of a slider in a HMI" loading="lazy" decoding="async" src="https://flowfuse.com/img/slider-ui-g-ru3pSeKO-540.webp" width="540" height="164" /></picture></p>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-119" style="height: 300px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow119 = "\n[{\"id\":\"05340e7a133098a6\",\"type\":\"ui_slider\",\"z\":\"0e6be5088cecccc1\",\"name\":\"\",\"label\":\"\",\"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\":[],\"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\":[{\"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\":[{\"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 meters per minute\",\"layout\":\"row-spread\",\"className\":\"\",\"style\":false,\"font\":\"\",\"fontSize\":16,\"color\":\"#000000\",\"x\":350,\"y\":340,\"wires\":[]},{\"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}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow119.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-119') })</script>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>. You can also read some of our previous Node-RED tips using the links below.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/">Node-RED Tips - Subflows, Link Nodes, and the Range Node</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/">Node-RED Tips - Importing, Exporting, and Grouping Flows</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/">Node-RED Tips - Smooth, Catch, and Maths</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/">Node-RED Tips - Exec, Filter, and Debug</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/">Node-RED Tips - Deploying, Debugging, and Delaying</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/">Node-RED Tips - Wiring Shortcuts</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/node-red-community-survey-results/Node-RED Community Survey ResultsDiscover the trends and insights into the Node-RED community2023-05-31T00:00:00Z<p>The Node-RED community recently published the results of their <a href="https://nodered.org/about/community/survey/2023/">2023 Community Survey</a>, building upon their <a href="https://nodered.org/about/community/survey/2019/">2019 survey</a>. The findings reveal some interesting trends within the Node-RED community that are worth highlighting.</p>
<!--more-->
<ol>
<li>
<p><strong>Passionate and Experienced Community</strong>. 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.</p>
<p>Developer tools can fall out of fashion but it is clear Node-RED is providing value and is being used by developers.</p>
</li>
<li>
<p><strong>Increasing Adoption in Industrial and Manufacturing Automation</strong>. 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/PLC devices, which grew from 24% to 35.8% in 2023. Furthermore, the adoption of popular manufacturing industry protocols like <a href="https://opcfoundation.org/">OPC-UA</a> and <a href="https://modbus.org/">Modbus</a> has also increased, with OPC-UA usage rising from 9.3% to 16.7% and Modbus usage increasing from 15.8% to 27.6%.</p>
</li>
<li>
<p><strong>InfluxDB dominance in the Node-RED community</strong>.<a href="https://www.influxdata.com/"> InfluxDB</a> 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%.</p>
<p>It is evident that the MING stack (MQTT/Mosquitto, InfluxDB, Node-RED, and Grafana) is gaining momentum and becoming a preferred choice for developers.</p>
</li>
<li>
<p><strong>Limitations to Node-RED Adoption</strong>. 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%).</p>
<p>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.</p>
</li>
</ol>
<p>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 <a href="https://nodered.org/about/community/survey/2023/">nodered.org website</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/Persisting chart data in Node-RED Dashboard 1Keep your historic chart data safe and available2023-05-25T00:00:00ZRob Marcer<p>Node-RED makes it easy to create HMI (Human Machine Interfaces) using <a href="https://flows.nodered.org/node/node-red-dashboard">Node-RED Dashboard</a>.</p>
<!--more-->
<p>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.</p>
<div class="blog-update-notes">
<p><strong>UPDATE:</strong> Since this article was published, Node-RED Dashboard (1.0) has been <a href="https://discourse.nodered.org/t/announcement-node-red-dashboard-v1-deprecation-notice/89006" target="_blank">deprecated</a>.</p>
<p>Instead, it is recommended to use <a href="https://dashboard.flowfuse.com/">FlowFuse Dashboard (Dashboard 2.0)</a> which is a more modern and feature-rich dashboard solution for Node-RED.</p>
</div>
<h3 id="the-importance-of-persisting-chart-data" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#the-importance-of-persisting-chart-data"></a> The Importance of Persisting Chart Data</h3>
<p>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.</p>
<p>How can we store our chart data so we can be confident it will be there each time a user views your HMI?</p>
<h3 id="example-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#example-dashboard"></a> Example Dashboard</h3>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-110" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow110 = "\n[{\"id\":\"c6825b1001216b89\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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";
new FlowRenderer().renderFlows(JSON.parse(flow110.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-110') })</script>
x
### How can we store and recall the chart data?
<p>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.</p>
<p>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.</p>
<h4 id="1.-node-red-file-out-and-file-in-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#1.-node-red-file-out-and-file-in-nodes"></a> 1. Node-RED file-out and file-in nodes</h4>
<p>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.</p>
<p>This example flow shows how to use the file-out node to write the chart data to your local filesystem.</p>
<div id="nr-flow-111" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow111 = "\n[{\"id\":\"ead9df683d29fb8a\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[]},{\"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";
new FlowRenderer().renderFlows(JSON.parse(flow111.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-111') })</script>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-112" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow112 = "\n[{\"id\":\"ead9df683d29fb8a\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[]},{\"id\":\"e9cb9350f1aaeb38\",\"type\":\"inject\",\"z\":\"668c56888fd0f960\",\"name\":\"import data\",\"props\":[],\"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\":[]},{\"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";
new FlowRenderer().renderFlows(JSON.parse(flow112.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-112') })</script>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/inject-on-deploy-8POKAssRX9-510.avif 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/inject-on-deploy-8POKAssRX9-510.webp 510w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Import data on deploy" alt="Import data on deploy" loading="lazy" decoding="async" src="https://flowfuse.com/img/inject-on-deploy-8POKAssRX9-510.jpeg" width="510" height="518" /></picture></p>
<p>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.</p>
<h4 id="2.-flowfuse's-persistent-context" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#2.-flowfuse's-persistent-context"></a> 2. FlowFuse's persistent context</h4>
<p>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.</p>
<p>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.</p>
<div id="nr-flow-113" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow113 = "\n[{\"id\":\"c6825b1001216b89\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[{\"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\":[]},{\"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";
new FlowRenderer().renderFlows(JSON.parse(flow113.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-113') })</script>
<p>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.</p>
<div id="nr-flow-114" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow114 = "\n[{\"id\":\"c6825b1001216b89\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[{\"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\":[]},{\"id\":\"3379276c77b4691c\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"import data\",\"props\":[],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"x\":150,\"y\":180,\"wires\":[[\"ddd1ef41321fb4a6\"]]},{\"id\":\"ddd1ef41321fb4a6\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"rules\":[{\"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\":[]},{\"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";
new FlowRenderer().renderFlows(JSON.parse(flow114.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-114') })</script>
<p>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.</p>
<h4 id="3.-expose-the-data-via-an-api-then-manually-import-it" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#3.-expose-the-data-via-an-api-then-manually-import-it"></a> 3. Expose the data via an API then manually import it</h4>
<p>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.</p>
<div id="nr-flow-115" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow115 = "\n[{\"id\":\"6523e86042252710\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[{\"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\":\"/data\",\"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\":[]},{\"id\":\"b4f0eb085cc91834\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"Get the chart data from flow.chart-data\",\"rules\":[{\"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}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow115.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-115') })</script>
<p>We can now access the data by simply visiting the URL of the API.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/data-in-browser-J_qA5RRsz7-621.avif 621w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/data-in-browser-J_qA5RRsz7-621.webp 621w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The chart data accessed via the API in a web browser" alt="The chart data accessed via the API in a web browser" loading="lazy" decoding="async" src="https://flowfuse.com/img/data-in-browser-J_qA5RRsz7-621.jpeg" width="621" height="147" /></picture></p>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/">blog post here</a>.</p>
<p>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'.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/inject-the-json-8Pn1gTdION-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/inject-the-json-8Pn1gTdION-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Inject the data in JSON format" alt="Inject the data in JSON format" loading="lazy" decoding="async" src="https://flowfuse.com/img/inject-the-json-8Pn1gTdION-650.jpeg" width="650" height="774" /></picture></p>
<div id="nr-flow-116" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow116 = "\n[{\"id\":\"6523e86042252710\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"\",\"props\":[],\"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\":[\"#1f77b4\",\"#aec7e8\",\"#ff7f0e\",\"#2ca02c\",\"#98df8a\",\"#d62728\",\"#ff9896\",\"#9467bd\",\"#c5b0d5\"],\"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\":[]},{\"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\":[{\"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\":\"/data\",\"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\":[]},{\"id\":\"b4f0eb085cc91834\",\"type\":\"change\",\"z\":\"4767c2f7095bee53\",\"name\":\"Get the chart data from flow.chart-data\",\"rules\":[{\"t\":\"set\",\"p\":\"payload\",\"pt\":\"msg\",\"to\":\"chart-data\",\"tot\":\"flow\"}],\"action\":\"\",\"property\":\"\",\"from\":\"\",\"to\":\"\",\"reg\":false,\"x\":440,\"y\":660,\"wires\":[[\"7ca49e5465699355\"]]},{\"id\":\"d13d16b33ec638b2\",\"type\":\"inject\",\"z\":\"4767c2f7095bee53\",\"name\":\"import data\",\"props\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"[{\\\"series\\\":[\\\"\\\"],\\\"data\\\":[[{\\\"x\\\":1684841975036,\\\"y\\\":8},{\\\"x\\\":1684841976037,\\\"y\\\":6},{\\\"x\\\":1684841977038,\\\"y\\\":7},{\\\"x\\\":1684841978037,\\\"y\\\":7}]],\\\"labels\\\":[\\\"\\\"]}]\",\"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}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow116.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-116') })</script>
<p>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.</p>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/persisting-chart-data-in-node-red/#conclusion"></a> Conclusion</h3>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/Best Practices Integrating a Modbus Device With Node-REDIntegrate Modbus with Node-RED2023-05-16T00:00:00Z<p>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.</p>
<!--more-->
<p>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.</p>
<p>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:</p>
<h3 id="add-watchdogs-to-node-red-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#add-watchdogs-to-node-red-flows"></a> Add Watchdogs to Node-RED Flows</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-3-f6a8PYlGTC-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-3-f6a8PYlGTC-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example watchdog flow" alt="Example watchdog flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-3-f6a8PYlGTC-650.jpeg" width="650" height="158" /></picture></p>
<p>Each of the trigger nodes are setup with similar parameters, only the delay values differ.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-14-fs5fznKpWS-504.avif 504w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-14-fs5fznKpWS-504.webp 504w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Trigger node configuration" alt="Trigger node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-14-fs5fznKpWS-504.jpeg" width="504" height="580" /></picture></p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-10-XnEUdOl20m-508.avif 508w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-10-XnEUdOl20m-508.webp 508w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Change node configuration" alt="Change node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-10-XnEUdOl20m-508.jpeg" width="508" height="305" /></picture></p>
<h3 id="choosing-a-safe-poll-rate" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#choosing-a-safe-poll-rate"></a> Choosing a Safe Poll Rate</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-7-dvJrM-bPkB-638.avif 638w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-7-dvJrM-bPkB-638.webp 638w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Modbus node configuration" alt="Modbus node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-7-dvJrM-bPkB-638.jpeg" width="638" height="579" /></picture></p>
<h3 id="coil%2Fregister-grouping" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#coil%2Fregister-grouping"></a> Coil/Register Grouping</h3>
<p>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/false 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-5-DhQsCJ4DmR-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-5-DhQsCJ4DmR-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using a switch node to separate the messages" alt="Using a switch node to separate the messages" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-5-DhQsCJ4DmR-650.jpeg" width="650" height="321" /></picture></p>
<p>The filter node blocks redundant data from triggering subsequent nodes.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-12-gz6pP3MHqg-502.avif 502w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-12-gz6pP3MHqg-502.webp 502w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Filter node configuration" alt="Filter node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-12-gz6pP3MHqg-502.jpeg" width="502" height="332" /></picture></p>
<p>The function node, “modbusMapArray,” creates messages that are much more user-friendly in the Node-RED environment.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-1-6QTaCpI4_f-480.avif 480w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-1-6QTaCpI4_f-480.webp 480w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Modbus function node" alt="Modbus function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-1-6QTaCpI4_f-480.jpeg" width="480" height="369" /></picture></p>
<p>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.</p>
<h3 id="reading-data-types" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#reading-data-types"></a> Reading Data Types</h3>
<p>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, [21035,17562]. I can simulate this with the <a href="https://www.automationdirect.com/adc/overview/catalog/software_products/programmable_controller_software/productivity_suite_programming_software">Productivity Suite Programming Software</a> 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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-11-1m6PlssSX0-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-11-1m6PlssSX0-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Productivity Suite Programming Software" alt="Productivity Suite Programming Software" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-11-1m6PlssSX0-650.jpeg" width="650" height="180" /></picture></p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-4-nP2o5Cqoi_-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-4-nP2o5Cqoi_-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using msg.responseBuffer.buffer to retrieve each octet" alt="Using msg.responseBuffer.buffer to retrieve each octet" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-4-nP2o5Cqoi_-650.jpeg" width="650" height="341" /></picture></p>
<p>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).</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-8-qb3m74O87u-643.avif 643w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-8-qb3m74O87u-643.webp 643w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Modbus node configuration" alt="Modbus node configuration" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-8-qb3m74O87u-643.jpeg" width="643" height="581" /></picture></p>
<p>In this example the byte order from msg.responseBuffer.buffer doesn’t quite match the data type, so we have to rebuild the buffer.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-13-eweuMnerwW-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-13-eweuMnerwW-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Mapping the data in a function node" alt="Mapping the data in a function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-13-eweuMnerwW-650.jpeg" width="650" height="380" /></picture></p>
<p>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 <a href="https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html">https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html</a>.</p>
<h3 id="general-architecture" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#general-architecture"></a> General Architecture</h3>
<p>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.</p>
<h3 id="security" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#security"></a> Security</h3>
<p>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 <a href="https://nodered.org/docs/user-guide/runtime/securing-node-red">Securing Node-RED</a>. 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://<yourIP>:1880/careful?value=true&fc=15&unitid=1&address=0&quantity=10 and remotely turn on and off whatever they wanted.</yourIP></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-6-IlBo-XIeSX-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-6-IlBo-XIeSX-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example endpoint flow" alt="Example endpoint flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-6-IlBo-XIeSX-650.jpeg" width="650" height="125" /></picture></p>
<p>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://<yourIP>:1880/honkTheLunchHorn?honk=true</yourIP></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-2-MBwGYVknXw-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-2-MBwGYVknXw-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Locking down the endpoint" alt="Locking down the endpoint" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-2-MBwGYVknXw-650.jpeg" width="650" height="127" /></picture></p>
<h3 id="final-thoughts" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/integrating-modbus-with-node-red/#final-thoughts"></a> Final Thoughts</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/integrating-modbus-9-u3WWUnJO-p-642.avif 642w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/integrating-modbus-9-u3WWUnJO-p-642.webp 642w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Logging failures" alt="Logging failures" loading="lazy" decoding="async" src="https://flowfuse.com/img/integrating-modbus-9-u3WWUnJO-p-642.jpeg" width="642" height="353" /></picture></p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/FlowFuse 1.7 Now Available with Remote Node-RED Editor AccessFurther improving fleet management and maintenance of remote Node-RED instances2023-05-11T00:00:00Z<p>FlowFuse 1.7 adds new support for accessing the Node-RED Editor on Devices via FlowFuse.</p>
<!--more-->
<h2 id="further-improving-fleet-management-and-maintenance-of-remote-node-red-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#further-improving-fleet-management-and-maintenance-of-remote-node-red-instances"></a> Further improving fleet management and maintenance of remote Node-RED instances</h2>
<p>We are excited to introduce a new feature that will simplify the process of debugging and developing flows for devices. Our latest feature, "Editing Flows on Devices" allows users to access the editor directly on their device without the need for complex network configurations or firewalls. This feature will significantly improve the user experience, making it easier and more efficient to work with devices.</p>
<p>This update is a part of our ongoing commitment to making FlowFuse the best possible solution for developing your Node-RED flows, no matter where they're running. In fact, as part of our last release 1.6, we already introduced the feature: <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#access-node-red-logs-from-remote-devices">"Access Node-RED logs from remote devices"</a>. This feature made it easy for users to troubleshoot and debug. Building on that, we've taken the next step, and it's now possible to access the Device Editor.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/zS6P3RR86vE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="device-status-visualisation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#device-status-visualisation"></a> Device Status Visualisation</h2>
<p>This FlowFuse version upgrades device monitoring. It's made easier to manage your devices effectively, especially when there's many of them. Now we offer an intuitive, user-friendly method for users to keep an eye on their devices' status and evaluate the health of their team's devices overall. Creating an overview of your fleet's health, however large your fleet might be.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/S--viuPhrS8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="auto-restart-for-hung-node-red-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#auto-restart-for-hung-node-red-instances"></a> Auto Restart for Hung Node-RED Instances</h2>
<p>This enhancement ensures a more robust and reliable experience when working with Node-RED flows. The launcher now actively monitors the Node-RED process to detect if it has become unresponsive or hung, in addition to the existing checks for start-up and unexpected process exits. This advancement takes us one step further in improving the availability of our Node-RED instances.</p>
<h2 id="ongoing-topics" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#ongoing-topics"></a> Ongoing Topics</h2>
<h3 id="high-availability" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#high-availability"></a> High Availability</h3>
<p>We're actively progressing on the topic of enhancing High Availability in FlowFuse for Node-RED. Our initial tasks and modifications have been identified, specifically pertaining to the operation of Node-RED instances within the k8s environment. These adjustments are aimed at constructing a more resilient system. Stay tuned for a comprehensive update regarding our advancements in High Availability.</p>
<h3 id="soc2-certification" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#soc2-certification"></a> SOC2 Certification</h3>
<p>Dedicated to upholding the highest levels of security and privacy, our company acknowledges the significance of industry-standard certifications like SOC2 in fostering trust with our customers and partners. We aim to achieve SOC2 Type 1 certification by the end of Q2 and subsequently maintain a continuous SOC2 Type 2 certification. We will keep you informed on our progress as we reach essential milestones in our SOC2 certification journey. Rest assured, our commitment to delivering the utmost security and privacy for our customers and partners remains unwavering.</p>
<h3 id="aws-marketplace-onboarding" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#aws-marketplace-onboarding"></a> AWS Marketplace onboarding</h3>
<p>We are excited to provide an update on our ongoing task of onboarding Node-RED instances to AWS Marketplace via FlowFuse Cloud, which we have started in this Iteration. By offering Node-RED instances through AWS Marketplace, we aim to simplify the deployment process for our customers.</p>
<p>One of the significant challenges we are currently addressing is handling our current payment system in parallel with a new method. This will ensure a seamless billing experience for our customers, as they will be able to manage their Node-RED instance subscriptions through their existing AWS accounts.</p>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<ul>
<li><a href="https://github.com/andreikop">@andreikop</a> for their work on the <a href="https://github.com/FlowFuse/flowforge-driver-k8s/pull/80">flowforge-driver-k8s #80</a> and <a href="https://github.com/FlowFuse/helm/pull/125">flowforge/helm #125</a></li>
<li><a href="https://github.com/elenaviter">@elenaviter</a> for their work on <a href="https://github.com/FlowFuse/helm/pull/126">flowforge/helm #126</a></li>
</ul>
<p>As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h2 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#what's-next%3F"></a> What's next?</h2>
<p>We're always working to enhance your experience with FlowFuse. Here's how you can stay informed and contribute:</p>
<ul>
<li><strong>Roadmap Overview</strong>: Check out our [Product Roadmap Page/changelog/) to see what we're planning for future updates.</li>
<li><strong>Entire Roadmap</strong>: Visit our <a href="https://github.com/orgs/FlowFuse/projects/5">Roadmap on GitHub</a> to follow our progress and contribute your ideas.</li>
<li><strong>Feedback</strong>: We're interested in your thoughts about FlowFuse. Your feedback is crucial to us, and we'd love to hear about your experiences with the new features and improvements. Please share your thoughts, suggestions, or report any <a href="https://github.com/FlowFuse/flowfuse/issues/new/choose">issues on GitHub</a>.</li>
</ul>
<p>Together, we can make FlowFuse better with each release!</p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#bug-fixes"></a> Bug Fixes</h2>
<p>Incorrect number of days displayed when adding a new license <a href="https://github.com/FlowFuse/flowfuse/issues/1895">#1895</a></p>
<p>Users were unable to upgrade modules in Manage Palette, even after restarting Node-RED. <a href="https://github.com/FlowFuse/flowfuse/issues/2005">#2005</a></p>
<p>Enable 'Delete Team' Button <a href="https://github.com/FlowFuse/flowfuse/issues/2031">#2031</a></p>
<p>Triggering a Node-RED restart using the action button resulted in two instances of Node-RED running in the container, causing one instance to crash due to port 1880 being already in use. <a href="https://github.com/FlowFuse/flowfuse/issues/1860">#2031</a></p>
<p>Error deleting instance with missing subscription <a href="https://github.com/FlowFuse/flowfuse/issues/2080">#2080</a></p>
<p>Snapshot Rollback no longer working <a href="https://github.com/FlowFuse/flowfuse/issues/2026">#2026</a></p>
<p>Users receiving an unauthorized error when attempting to switch to a team in which they are a member <a href="https://github.com/FlowFuse/flowfuse/issues/1845">#1845</a></p>
<p>Cannot select "Member" option when inviting a team member <a href="https://github.com/FlowFuse/flowfuse/issues/2084">#2084</a></p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.7.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/flowforge-1-7-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That's also a great place to send us any feedback or feature requests.</p>
<p>You can also get help on <a href="https://discourse.nodered.org/">the Node-RED forums</a></p>
<p>As well as in the <a href="https://github.com/FlowFuse/flowfuse/discussions">forum within our Github project</a></p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a></p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/Community News May 2023Your monthly update for the FlowFuse and Node-RED communities2023-05-04T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter for May 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#upcoming-events"></a> Upcoming events</h2>
<h3 id="ask-me-anything-about-debugging-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#ask-me-anything-about-debugging-node-red"></a> Ask Me Anything about Debugging Node-RED</h3>
<p>Our monthly Node-RED AMA session will have a special focus on debugging. Nick and Rob will lead us through some useful debug workflows to show how they approach debugging Node-RED applications. During the live coding sessions there will be opportunities for attendees to ask questions in real-time. Join us to learn from the experts on the tips and tricks for debugging Node-RED flows. <a href="https://flowfuse.com/ask-me-anything/ama-nodered-may/">Sign-up today to participate</a>.</p>
<h3 id="getting-started-with-node-red-dashboard" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#getting-started-with-node-red-dashboard"></a> Getting Started with Node-RED Dashboard</h3>
<p>How can you use Node-RED to create dashboards and interactive graphs of your data? The answer is the Node-RED Dashboard node, the most popular node in the Node-RED community. In this webinar, Rob Marcer will take you through the steps of how to get started with the Node-RED Dashboard. <a href="https://flowfuse.com/webinars/2023/getting-started-nodered-dashboard/">Register today</a>.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#from-our-blog"></a> From our Blog</h2>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/">Chat GPT in Node-RED Function Nodes</a> - Use Chat GPT to write Node-RED functions directly in the Node-RED interface.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/">Securing Node-RED</a> - A look at how you can secure Node-RED deployments.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/nodered-community-health/">Node-RED Community Health</a> - Some key community metrics for the Node-RED community.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/hannover-messe/">FlowFuse's visit to Hannover Messe 2023</a> - Our CEO and Product Manager visited Hannover Messe in Germany; one of the largest trade shows for manufacturing.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/">FlowFuse 1.6 Now Available</a> - FlowFuse 1.6 included support for multi-instance Node-RED within a single application as well as support for logging from remote devices.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/">Node-RED Tips - Subflows, Link Nodes, and the Range Node</a></p>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#from-the-community"></a> From the Community</h2>
<p>Jsonata is a very useful and often underutilised tool built into Node-RED. Steve over at <a href="https://stevesnoderedguide.com/">Steve's Node-RED Guide</a> has published a great beginners guide. If you are new to Jsonata and want to learn more we recommend you <a href="https://stevesnoderedguide.com/node-red-and-jsonata-for-beginners">take a look</a>.</p>
<h3 id="custom-node-spotlight---node-red-contrib-queue-gate" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#custom-node-spotlight---node-red-contrib-queue-gate"></a> Custom Node Spotlight - node-red-contrib-queue-gate</h3>
<p><a href="https://flows.nodered.org/node/node-red-contrib-queue-gate">Queue Gate</a> is a handy custom node which allows you to control the flow of messages. You might wish to queue up all the messages in a flow and then release them all, once an hour. Maybe you want to release just one message at a time and wait until the prior message completed a section of your flow. Queue Gate makes message queuing really easy without the need to use an external queue solution.</p>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/community-news-05/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li>
<p><strong><a href="https://boards.greenhouse.io/flowfuse/jobs/4798023004">Developer Advocate - Manufacturing & Industrial Automation</a></strong></p>
</li>
<li>
<p><strong><a href="https://boards.greenhouse.io/flowfuse/jobs/4843566004">Sales Representative</a></strong></p>
</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/Chat GPT in Node-RED Function NodesNew Node-RED function with embedded ChatGPT is now open-sourced and available to use!2023-05-02T12:00:00ZJoe PavittSteve McLaughlin<p>Recently we <a href="https://www.linkedin.com/posts/flowforge_chatgpt-with-node-red-function-nodes-activity-7052725869684953088-2yOA?utm_source=share&utm_medium=member_desktop">posted a demo of ChatGPT integration in a Node-RED function node</a>
onto our social media accounts. We have now <a href="https://github.com/FlowFuse/node-red-function-gpt" target="_blank">open-sourced</a> this for all to play with, and <strong>welcome any and all contributions</strong>.</p>
<!--more-->
<h2 id="how-it-works---prompt-engineering" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/#how-it-works---prompt-engineering"></a> How it Works - Prompt Engineering</h2>
<p>OpenAI make a collection of their <a href="https://platform.openai.com/docs/models">Generative AI models</a> available
via an API. We are wrapping OpenAI's <a href="https://www.npmjs.com/package/openai">node.js module</a>, and in particular
using the <code>openai.createChatCompletion()</code> functionality. For this API, you provide a chat history, and ChatGPT will
respond with the next entry in that conversation.</p>
<p>In order to "train" ChatGPT for our use case of populating Node-RED function nodes, we first tried a collection of prompts, defining specific
requirements for the contents, e.g. <em>"Always write Javascript"</em>, <em>"Never include the wrapping function definition"</em>,
<em>"Assume the input is always msg"</em>.</p>
<p>It turns out though, that we were over-engineering it, we were not getting reliable results and ended up
realising that ChatGPT's existing knowledge of Node-RED was sufficient such that we could use that as a prompt:</p>
<p>Here's what we settled on:</p>
<div style="position: relative" id="code-container-19">
<pre class="language-javascript"><code id="code-19" class="language-javascript"><span class="token literal-property property">messages</span><span class="token operator">:</span> <span class="token punctuation">[</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">role</span><span class="token operator">:</span> <span class="token string">"system"</span><span class="token punctuation">,</span> <span class="token literal-property property">content</span><span class="token operator">:</span> <span class="token string">"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."</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br /> <span class="token punctuation">{</span><span class="token literal-property property">role</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span> <span class="token literal-property property">content</span><span class="token operator">:</span> prompt<span class="token punctuation">}</span><br /><span class="token punctuation">]</span><span class="token punctuation">,</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-19" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Here we send a <code>system</code> prompt in order to setup ChatGPT, and then follow that immediately with whatever the user has typed.
From our (limited) testing, this has given us fairly reliable results.</p>
<p>Breaking this prompt down:</p>
<ul>
<li><em><strong>"Always respond with content for a Node-RED function node"</strong></em>: Ensured no surrounding <code>function () {}</code> definition and set expectations that the function would deal with a <code>msg</code> and likely <code>msg.payload</code> object.</li>
<li><em><strong>"Don't add any commentary"</strong></em>: ChatGPT likes to, well, chat. It would always return raw text justifying decisions, etc. Here, we just wanted the code.</li>
<li><em><strong>"Always use const or let instead of var"</strong></em>: This was Steve being picky.</li>
<li><em><strong>"Always return msg, unless told otherwise"</strong></em>: We found this wasn't mostly required, but occasionally it would try to return a different variable, and we'd lose context of <code>msg.payload</code>, or other data stored in <code>msg</code>. So this just made sure we had the consistency.</li>
</ul>
<p>The response from this API call is then populated into the contents of the active tab in the function node:</p>
<img width="1728" alt="Screenshot 2023-04-21 at 16 08 47" src="https://user-images.githubusercontent.com/99246719/233671631-fefa36c1-6db4-4392-a057-314c16fd91b7.png" />
<p>In order to use it yourself, you will need a <a href="https://platform.openai.com/account/api-keys">valid API Key from OpenAI</a>.</p>
<h2 id="additional-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/#additional-features"></a> Additional Features</h2>
<p>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
<a href="https://github.com/FlowFuse/node-red-function-gpt">open-sourced</a> 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!</p>
<ul>
<li>
<p><strong>Insert at Cursor (<a href="https://github.com/FlowFuse/node-red-function-gpt/issues/11">issue</a>):</strong> Currently, the Ask GPT call will replace <em>all</em> of the content of that tab. Would be great
to have the code insert wherever the cursor last was in order to add to existing code.</p>
</li>
<li>
<p><strong>Retain Conversation History (<a href="https://github.com/FlowFuse/node-red-function-gpt/issues/12">issue</a>):</strong> Each time a new prompt is provided by the Node-RED user, we send a fresh conversation to OpenAI,
meaning that knowledge of previously asked questions are not retained.</p>
</li>
<li>
<p><strong>Client side ChatGPT Config (<a href="https://github.com/FlowFuse/node-red-function-gpt/issues/13">issue</a>):</strong> Currently, when you add a new "function-gpt" node you need to select the ChatGTP
Config node and click "Deploy" before you can ask it a question. Our ChatGPT interaction operates server-side (to
protect your API key), so Node-RED needs that in the runtime first, before a call to ChatGPT can be made. Ideally,
we'd be smarter here and pass client-side creds along with the call such that we can use any changes made by the
user at the time of the call.</p>
</li>
</ul>
<h2 id="flowfuse-assistant---no-api-keys-required!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/chatgpt-nodered-fcn-node/#flowfuse-assistant---no-api-keys-required!"></a> FlowFuse Assistant - No API Keys Required!</h2>
<p>Great news! You no longer need to manage OpenAI API keys or configure ChatGPT nodes. The <a href="https://flowfuse.com/docs/user/assistant/">FlowFuse Assistant</a> is now built directly into Node-RED on FlowFuse Cloud, making AI-powered development even easier.</p>
<p>Available on FlowFuse Cloud, the Assistant offers:</p>
<ul>
<li><strong>Quick Function Node Creation</strong>: Add function nodes to your flow without dragging from the palette</li>
<li><strong>In-line Code Generation</strong>: Generate JavaScript code for function nodes, JSON for JSON editors, and Vue.js for FlowFuse Dashboard ui-template widgets</li>
<li><strong>Flow Explainer</strong>: Select nodes and click "Explain Flows" to understand what they do</li>
</ul>
<p>FlowFuse Assistant helps developers work faster and smarter with Node-RED. <a href="https://app.flowfuse.com/account/create">Start your free trial</a> to experience AI-powered Node-RED development on FlowFuse Cloud.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/Running the FlowFuse Device Agent as a service on a Raspberry PiStep by step guide to run the device agent as a service2023-05-02T00:00:00ZRob Marcer<p>FlowFuse's device agent allows you to manage and run your Node-RED instances on
your own hardware such as a Raspberry Pi. This can be very useful where an
application you've written needs to run flows with direct access to hardware sensors.</p>
<p>In this article I'm going to explain the steps to configure our device agent to run as a service in Raspbian OS,
or any other OS that uses systemd.</p>
<!--more-->
<h2 id="why-run-the-device-agent-as-a-service%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#why-run-the-device-agent-as-a-service%3F"></a> Why run the device agent as a service?</h2>
<p>The standard process for running FlowFuse's device agent is to start it on the
command line using the command <code>flowforge-device-agent</code>. This works fine for testing
but for long-term installations it's useful to run the device agent as a service.
Once running as a service, the device agent will continue to run even if your
device is restarted or your SSH connection to your Pi fails.</p>
<h2 id="set-up-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#set-up-steps"></a> Set up steps</h2>
<h3 id="create-the-service-file" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#create-the-service-file"></a> Create the Service File</h3>
<p>The first step is creating the systemd unit file for your service. You can start by creating a new file in the
<code>/etc/systemd/system</code> directory with a .service file extension:</p>
<p><code>sudo nano /etc/systemd/system/flowforge-device-agent.service</code></p>
<h3 id="define-the-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#define-the-service"></a> Define the Service</h3>
<p>In the service file, you'll need to define the following parameters:</p>
<ul>
<li><code>Description</code>: A brief description of what the service does.</li>
<li><code>ExecStart</code>: The command(s) to execute to start the service.</li>
<li><code>User and Group</code>: The user and group that the service runs as.</li>
<li><code>Type</code>: Whether the service is a simple or a forking type.</li>
</ul>
<p>We've created the content you'll need for this file and shared it via <a href="https://github.com/FlowFuse/device-agent/blob/main/service/flowfuse-device.service">this GitHub page</a>.</p>
<p>Copy the code from that page into the nano window you created in step 1, then save and exit out of nano.</p>
<h3 id="starting-the-service-on-boot-(optional)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#starting-the-service-on-boot-(optional)"></a> Starting the service on boot (optional)</h3>
<p>If you want Node-RED to run when the Pi is turned on, or re-booted, you can enable the service to autostart by running the command:</p>
<p><code>sudo systemctl enable flowforge-device-agent.service</code></p>
<p>To disable the service, run the command:</p>
<p><code>sudo systemctl disable flowforge-device-agent.service</code></p>
<h3 id="using-your-new-service" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#using-your-new-service"></a> Using your new service</h3>
<p>You can now start your service with the start command:</p>
<p><code>sudo systemctl start flowforge-device-agent</code></p>
<p>You can check the current status with the status command:</p>
<p><code>sudo systemctl status flowforge-device-agent</code></p>
<p>Finally, if you need to stop your agent you can do so with the command:</p>
<p><code>sudo systemctl stop flowforge-device-agent</code></p>
<h2 id="further-reading" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/05/device-agent-as-a-service/#further-reading"></a> Further reading</h2>
<p>If you'd like to learn about using services via the systemctl command you can access
the help text by running <code>systemctl -h</code> from your Pi terminal.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/Node-RED Tips - Subflows, Link Nodes, and the Range NodeSave yourself time when working on Node-RED with these three tips.2023-04-20T12:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-subflows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/#1.-subflows"></a> 1. Subflows</h3>
<p>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.</p>
<h4 id="why-use-subflows%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/#why-use-subflows%3F"></a> Why use subflows?</h4>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/no-subflow-so_eb_1yrd-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/no-subflow-so_eb_1yrd-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Duplication of the flow" alt="Duplication of the flow" loading="lazy" decoding="async" src="https://flowfuse.com/img/no-subflow-so_eb_1yrd-650.jpeg" width="650" height="114" /></picture></p>
<p>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.</p>
<h4 id="creating-a-subflow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/#creating-a-subflow"></a> Creating a subflow</h4>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/subflow-o35EcqkRGg-648.avif 648w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/subflow-o35EcqkRGg-648.webp 648w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Contents of the subflow" alt="Contents of the subflow" loading="lazy" decoding="async" src="https://flowfuse.com/img/subflow-o35EcqkRGg-648.jpeg" width="648" height="185" /></picture></p>
<p>You can now drop the subflow into your workspace as needed, saving space and making it easier to manage changes to your flow.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/using-the-subflow-RFYw0Gcbmg-549.avif 549w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/using-the-subflow-RFYw0Gcbmg-549.webp 549w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using the subflow to reduce duplication of flows" alt="Using the subflow to reduce duplication of flows" loading="lazy" decoding="async" src="https://flowfuse.com/img/using-the-subflow-RFYw0Gcbmg-549.jpeg" width="549" height="146" /></picture></p>
<h3 id="2.-link-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/#2.-link-nodes"></a> 2. Link Nodes</h3>
<p>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.</p>
<p>Formatting your flows into distinct sections using link nodes can make it easier to read and update your work.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/wiring-link-nodes-cpOBhO5AWV-532.gif 532w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Linking two link nodes together" alt="Linking two link nodes together" loading="lazy" decoding="async" src="https://flowfuse.com/img/wiring-link-nodes-cpOBhO5AWV-532.webp" width="532" height="164" /></picture></p>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-without-link-nodes-31s3ij8hgA-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-without-link-nodes-31s3ij8hgA-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="A flow without link nodes" alt="A flow without link nodes" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-without-link-nodes-31s3ij8hgA-650.jpeg" width="650" height="130" /></picture></p>
<p>It's easier to read and understand the flow once it's split up using the link nodes and groups.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-with-link-nodes-HtwYHBuz1O-606.avif 606w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-with-link-nodes-HtwYHBuz1O-606.webp 606w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The same flow as above, now split up using link nodes" alt="The same flow as above, now split up using link nodes" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-with-link-nodes-HtwYHBuz1O-606.jpeg" width="606" height="507" /></picture></p>
<h3 id="3.-range-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/3-quick-node-red-tips-6/#3.-range-node"></a> 3. Range Node</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flow-using-range-a94pDcg-0n-484.avif 484w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flow-using-range-a94pDcg-0n-484.webp 484w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example of using the range node" alt="Example of using the range node" loading="lazy" decoding="async" src="https://flowfuse.com/img/flow-using-range-a94pDcg-0n-484.jpeg" width="484" height="153" /></picture></p>
<p>To configure the node, set it up as follows:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/range-config-60KH-Yu6vB-487.avif 487w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/range-config-60KH-Yu6vB-487.webp 487w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Configuration of the range node" alt="Configuration of the range node" loading="lazy" decoding="async" src="https://flowfuse.com/img/range-config-60KH-Yu6vB-487.jpeg" width="487" height="332" /></picture></p>
<p>You should now see that the input values are translated to the appropriate value out of 100.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/range-demo-YJkC_wcNmy-534.gif 534w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The range note in use" alt="The range note in use" loading="lazy" decoding="async" src="https://flowfuse.com/img/range-demo-YJkC_wcNmy-534.webp" width="534" height="164" /></picture></p>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>. You can also read some of our previous Node-RED tips using the links below.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/">Node-RED Tips - Importing, Exporting, and Grouping Flows</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/">Node-RED Tips - Smooth, Catch, and Maths</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/">Node-RED Tips - Exec, Filter, and Debug</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/">Node-RED Tips - Deploying, Debugging, and Delaying</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/">Node-RED Tips - Wiring Shortcuts</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/hannover-messe/FlowFuse's visit to Hannover Messe 2023A recap of FlowFuse's visit to Hannover Messe2023-04-20T00:00:00Z<p>"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.</p>
<!--more-->
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#the-standard-for-edge-computing-and-plcs">arcticle</a> about the adoption of Node-RED for PLCs and Gateways.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/nodered-community-health/Node-RED Community HealthNode-RED community metrics2023-04-13T00:00:00Z<p>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.</p>
<!--more-->
<p><strong>GitHub Stars</strong></p>
<p>A popular method for demonstrating popularity of an open source project are GitHub stars. Node-RED has over 16K stars and when <a href="https://synodus.com/blog/low-code/open-source-low-code-platforms/">compared to other low-code platforms</a> 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.</p>
<p><strong>Node-RED Library</strong></p>
<p>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 <a href="https://flows.nodered.org/search?type=node&sort=downloads">4300 nodes available</a>, 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.</p>
<p><strong>Node-RED Website Traffic</strong></p>
<p>In the last year, Node-RED has had over 1.9 million unique visitors to the <a href="https://nodered.org/">nodered.org</a> 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.</p>
<p><strong>NPM Downloads & Docker Pull Requests</strong></p>
<p>Node-RED is installed by lots of people using Docker and NPM. The project averages over 500K/month pull requests from <a href="https://hub.docker.com/r/nodered/node-red">Docker Hub</a> and 100K/month pull requests on <a href="https://npm-stat.com/charts.html?package=node-red&from=2017-03-22&to=2023-03-22">NPM</a>.</p>
<p><strong>Node-RED Forums</strong></p>
<p>The <a href="https://discourse.nodered.org/">Node-RED community forum</a> 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.</p>
<p>Overall, the Node-RED community is large, healthy and engaged.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/FlowFuse v1.6 Now AvailableFlowFuse Now Supports Multi-Instance Node-RED for Complex Application Development2023-04-13T00:00:00Z<p>The new FlowFuse 1.6 adds new support for multi-instance Node-RED within a single application and support for logging from remote devices.</p>
<!--more-->
<h2 id="flowfuse-applications-can-now-support-multi-instance-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#flowfuse-applications-can-now-support-multi-instance-node-red"></a> FlowFuse Applications Can Now Support Multi-Instance Node-RED</h2>
<p>FlowFuse 1.6 expands the scope of applications to now allow for multiple instances of Node-RED. For complex Node-RED applications, it is common to have different flows interacting with other flows or flows deployed to different target environments. The ability to associate all these different flows with a single application makes it easier for the development, test and deployment of these types of complex applications.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/OHChdWeRI9Q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="access-node-red-logs-from-remote-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#access-node-red-logs-from-remote-devices"></a> Access Node-RED logs from remote devices</h2>
<p>FlowFuse makes it easy to deploy Node-RED out to remote devices. However, once Node-RED has been deployed to the remote device it is often difficult to troubleshoot or debug. Now with FlowFuse 1.6, you can get access to the Node-RED logs from remote devices. This makes it much easier to understand and debug the behavior of a remote device.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/yW1zxwiCmto" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="other-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#other-improvements"></a> Other Improvements</h2>
<p>Update email address verification <a href="https://github.com/FlowFuse/flowfuse/issues/813">#813</a></p>
<p>Reminder email about trial doesn't include a link to FF Cloud <a href="https://github.com/FlowFuse/flowfuse/issues/1815">#1815</a></p>
<p>Sign-up coupons improvement <a href="https://github.com/FlowFuse/flowfuse/issues/1788">#1788</a></p>
<p>New FF_Instance_* envvars inline with new terminology <a href="https://github.com/FlowFuse/flowfuse/issues/1844">#1844</a></p>
<p>Deprecate FF_PROJECT_* envvars <a href="https://github.com/FlowFuse/flowfuse/issues/1844">#1844</a></p>
<p>Integrate with PostHog events <a href="https://github.com/FlowFuse/flowfuse/pull/1922">#1922</a></p>
<p>Introduce search bar to docs/handbook <a href="https://github.com/FlowFuse/website/pull/620">#620</a></p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#bug-fixes"></a> Bug Fixes</h2>
<p>Deleting instances from the instance list fails <a href="https://github.com/FlowFuse/flowfuse/issues/1859">#1859</a></p>
<p>Removing old projects with missing subscriptions fails <a href="https://github.com/FlowFuse/flowfuse/issues/1837">#1837</a></p>
<p>Changing to a team as a member shows unauthorized error <a href="https://github.com/FlowFuse/flowfuse/issues/1845">#1845</a></p>
<p>Application Overview: “Open Editor” shouldn’t show (or should be disabled) if in “Starting” state <a href="https://github.com/FlowFuse/flowfuse/issues/1931">#1931</a></p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.6.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/flowforge-1-6-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That's also a great place to send us any feedback or feature requests.</p>
<p>You can also get help on <a href="https://discourse.nodered.org/">the Node-RED forums</a></p>
<p>As well as in the <a href="https://github.com/FlowFuse/flowfuse/discussions">forum within our Github project</a></p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a></p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/Securing Node-REDStep-by-step guide for securing your Node-RED projects.2023-04-05T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>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.</p>
<p>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.</p>
<h2 id="protecting-access-from-your-lan-to-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/#protecting-access-from-your-lan-to-node-red"></a> Protecting access from your LAN to Node-RED</h2>
<p>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.</p>
<p><code>http://192.168.0.3:1880</code></p>
<p>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.</p>
<p>One of the easiest ways to protect your flows is to add a username and password to your Node-RED instance.</p>
<p>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.</p>
<p><code>cd ~/.node-red</code></p>
<p>If you list that directory you should now see something like this:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ls-VheSi69Vkw-460.avif 460w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ls-VheSi69Vkw-460.webp 460w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Where your settings.js should show" alt="Where your settings file should show" loading="lazy" decoding="async" src="https://flowfuse.com/img/ls-VheSi69Vkw-460.jpeg" width="460" height="83" /></picture></p>
<p>We now need to edit settings.js, I'm going to use my favourite text editor, <a href="https://www.nano-editor.org/">Nano</a> to do that.</p>
<p><code>nano settings.js</code></p>
<p>We now need to find and edit the following section of the settings file:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/without-password-de1eEHQJqJ-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/without-password-de1eEHQJqJ-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The settings file before being edited" alt="The settings file before being edited" loading="lazy" decoding="async" src="https://flowfuse.com/img/without-password-de1eEHQJqJ-650.jpeg" width="650" height="158" /></picture></p>
<p>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.</p>
<p>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:</p>
<p><code>node-red admin hash-pw</code></p>
<p>Put in your new password, I'll use the password 'flowforge' in this example. The tool returns your password in a hashed format:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/password-gCQL2VRvN3-582.avif 582w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/password-gCQL2VRvN3-582.webp 582w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The Node-RED tool outputs the hashed password" alt="The Node-RED tool outputs the hashed password" loading="lazy" decoding="async" src="https://flowfuse.com/img/password-gCQL2VRvN3-582.jpeg" width="582" height="44" /></picture></p>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/with-password-d89DtLr4sL-635.avif 635w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/with-password-d89DtLr4sL-635.webp 635w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The settings file with the relevant section uncommented and the password set" alt="The settings file with the relevant section uncommented and the password set" loading="lazy" decoding="async" src="https://flowfuse.com/img/with-password-d89DtLr4sL-635.jpeg" width="635" height="170" /></picture></p>
<p>We can now save and exit out of the settings file.</p>
<p>The last step is to restart Node-RED, I'm using Debian so the command is:</p>
<p><code>node-red-restart</code></p>
<p>Now, when we try to access Node-RED I will need to provide a username and password.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/login-3GmZ2r8Qmg-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using the username and password to login to Node-RED" alt="Using the username and password to login to Node-RED" loading="lazy" decoding="async" src="https://flowfuse.com/img/login-3GmZ2r8Qmg-650.webp" width="650" height="437" /></picture></p>
<p>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.</p>
<p>Edit your settings.js file as explained above, look for the following section:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/editor-on-M3f-n-l62D-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/editor-on-M3f-n-l62D-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The setting file before turning off the editor" alt="The setting file before turning off the editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/editor-on-M3f-n-l62D-650.jpeg" width="650" height="77" /></picture></p>
<p>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:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/editor-off-0USfc9mOKz-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/editor-off-0USfc9mOKz-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The setting file after turning off the editor" alt="The setting file after turning off the editor" loading="lazy" decoding="async" src="https://flowfuse.com/img/editor-off-0USfc9mOKz-650.jpeg" width="650" height="85" /></picture></p>
<p>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.</p>
<p>Using these two features, we now have much better control over who can access the design interface for Node-RED.</p>
<h2 id="traffic-to-your-node-red-instance-is-unencrypted" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/#traffic-to-your-node-red-instance-is-unencrypted"></a> Traffic to your Node-RED instance is unencrypted</h2>
<p>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?</p>
<p>There are two obvious options, HTTPS, and a VPN (Virtual Private Network).</p>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/">hosting FlowFuse on Google Cloud</a>) but there is an easier way to get started, using a VPN.</p>
<p>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 <a href="https://tailscale.com/">Tailscale</a> which is free for personal projects.</p>
<p>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.</p>
<p>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.</p>
<p>The first step we need to take is creating a Tailscale account, <a href="https://login.tailscale.com/start">you can sign up for free here</a>. 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.</p>
<p>The install process is really easy, even on the Pi running Raspbian the steps you need to take are well explained in the <a href="https://tailscale.com/download/linux/debian-bullseye">Tailscale docs</a>.</p>
<p>For the Pi, these are the commands we need to run.</p>
<ol>
<li>Add Tailscale to the Apt package manager.</li>
</ol>
<p><code>curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null</code></p>
<p><code>curl -fsSL https://pkgs.tailscale.com/stable/debian/bullseye.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list</code></p>
<p><code>sudo apt-get update</code></p>
<ol start="2">
<li>Install Tailscale</li>
</ol>
<p><code>sudo apt-get install tailscale</code></p>
<ol start="3">
<li>Start Tailscale and connect your device</li>
</ol>
<p><code>sudo tailscale up</code></p>
<p>After running the last command, we need to follow the on screen prompts to link our devices to your VPN.</p>
<p>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 <a href="https://tailscale.com/kb/1028/key-expiry/">disabling your Tailscale key expiry</a>.</p>
<p>OK, now we've got our devices all connected you should see something like this in the Tailscale dashboard.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/tailscale-gWgiIEvXOI-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/tailscale-gWgiIEvXOI-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Tailscale dashboard showing my three devices" alt="Tailscale dashboard showing my three devices" loading="lazy" decoding="async" src="https://flowfuse.com/img/tailscale-gWgiIEvXOI-650.jpeg" width="650" height="260" /></picture></p>
<p>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:</p>
<p><code>http://100.71.28.60:1880</code></p>
<p>I’ve now secured all traffic between my devices and Node-RED project, I can access Node-RED from anywhere on the internet.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr-via-vpn-a-FM4nwfLO-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr-via-vpn-a-FM4nwfLO-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Accessing Node-RED via the VPN" alt="Accessing Node-RED via the VPN" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr-via-vpn-a-FM4nwfLO-650.jpeg" width="650" height="272" /></picture></p>
<p>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 <a href="https://nodered.org/docs/user-guide/runtime/securing-node-red">relevant docs on the Node-RED</a> website to gain some more ideas.</p>
<h2 id="what-about-hosting-node-red-on-a-cloud-solution-such-as-flowfuse%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/#what-about-hosting-node-red-on-a-cloud-solution-such-as-flowfuse%3F"></a> What about hosting Node-RED on a cloud solution such as FlowFuse?</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>FlowFuse has a <a href="https://app.flowfuse.com/account/create">free trial</a> if you'd like to see how we've made secure hosting of Node-RED easy.</p>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/securing-node-red-in-production/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/Community News April 2023Your monthly update for the FlowFuse and Node-RED communities2023-04-05T00:00:00Z<p>Welcome to the FlowFuse newsletter for April 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#upcoming-events"></a> Upcoming events</h2>
<h3 id="node-red-ask-me-anything" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#node-red-ask-me-anything"></a> Node-RED Ask Me Anything</h3>
<p>Back by popular demand, FlowFuse is hosting a monthly Node-RED Ask Me Anything session on April 13th. This is a great opportunity to ask Nick O’Leary, co-creator of Node-RED & FlowFuse CTO, and Rob Marcer, Node-RED FlowFuse Developer Educator your questions about Node-RED. <a href="https://flowfuse.com/ask-me-anything/ama-nodered-april/">Sign-up today to participate</a>.</p>
<h3 id="connect%2C-integrate%2C-visual-industrial-production-metrics-with-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#connect%2C-integrate%2C-visual-industrial-production-metrics-with-node-red"></a> Connect, Integrate, Visual Industrial Production Metrics with Node-RED</h3>
<p>Join Steve McLaughlin from FlowFuse as he showcases how easy it is to use Node-RED to visualize popular production metrics using Node-RED. <a href="https://flowfuse.com/webinars/2023/industrial-data-node-red/">Register today</a>.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#from-our-blog"></a> From our Blog</h2>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/">Comparing Node-RED Dashboards Solutions</a> - A popular article comparing Node-RED Dashboard, uibuilder and FlexDash.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/">IBM Cloud removes Node-RED starter application</a> - IBM has discontinued their Node-RED Starter Application, discover how to migrate to FlowFuse.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/">The benefits and drawbacks of using Node-RED function nodes</a> - Node-RED function nodes provide a great deal of flexibility in Node-RED. Discover the benefits and drawbacks of using them.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/">FlowFuse 15. Now Available</a> - FlowFuse 1.5 included updates to the UI and architecture to allow for future features.</p>
<p>Node-RED Quick Tips - Rob Marcer, FlowFuse Developer Educator has a weekly series of Node-RED hints and tips</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/">Node-RED Tips - Importing, Exporting, and Grouping Flows</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/">Node-RED Tips - Smooth, Catch, and Math</a></li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#from-the-community"></a> From the Community</h2>
<h3 id="quantum-for-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#quantum-for-node-red"></a> Quantum for Node-RED</h3>
<p>Discover how you can incorporate <a href="https://theailaboratory.wordpress.com/2023/03/24/quantum-for-everyone/">quantum technologies</a> into your Node-RED flows.</p>
<h3 id="image-recognition-within-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#image-recognition-within-node-red"></a> Image recognition within Node-RED</h3>
<p>Kazuhito Yokoi, a researcher at Hitachi, has published an <a href="https://kazuhitoyokoi.medium.com/sharing-node-red-flow-of-image-recognition-application-on-github-4d667cdea9f7">interesting article</a> detailing how to incorporate TensorFlow into a Node-RED application to do image recognition.</p>
<h3 id="custom-node-spotlight---node-red-contrib-web-worldmap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#custom-node-spotlight---node-red-contrib-web-worldmap"></a> Custom Node Spotlight - node-red-contrib-web-worldmap</h3>
<p>If you would like to include a map in your next project Worldmap is a really good place to start. You can pass coordinates in to set the map to a location or you can use the built in search tool to find a location. As a user manipulates the map a stream of updated coordinates can be passed back to your flows to trigger additional actions. It's a really useful tool, take a <a href="https://flows.nodered.org/node/node-red-contrib-web-worldmap">look here</a>.</p>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/04/community-news-04/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. Check out the current openings:</p>
<ul>
<li><strong><a href="https://boards.greenhouse.io/flowfuse/jobs/4798023004">Developer Advocate - Manufacturing & Industrial Automation</a></strong></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/IBM Cloud removes Node-RED starter applicationGet up and running with a Starter Application for Node-RED with FlowFuse or migrate your existing flows from IBM Cloud2023-03-29T12:00:00ZJoe Pavitt<p>IBM Cloud has <a href="https://www.ibm.com/cloud/blog/announcements/deprecation-of-ibm-cloud-starter-kits">recently announced</a> that they will no longer be providing their Cloud App Service Starter Kits, including the <a href="https://developer.ibm.com/tutorials/how-to-create-a-node-red-starter-application/">Node-RED Starter Application</a>.</p>
<!--more-->
<h2 id="node-red-starter-application" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#node-red-starter-application"></a> Node-RED Starter Application</h2>
<p>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.</p>
<p>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.</p>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/">Getting Started</a> guide.</p>
<p>You can sign up for a <a href="https://app.flowfuse.com/account/create">free FlowFuse Cloud Account</a> where you'll be given one small Node-RED instance for free, for your first month.</p>
<h2 id="integrations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#integrations"></a> Integrations</h2>
<p>If you've seen the excellent <a href="https://developer.ibm.com/tutorials/how-to-create-a-node-red-starter-application/">"Create a Node-RED starter application"</a> 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.</p>
<p>You can install the relevant nodes in one of two places:</p>
<ol>
<li><strong>Node-RED Palette Manager:</strong> Click "Menu > Manage Palette > Install". The menu is available via the icon in the top-right of your running Node-RED Instance)</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-650.avif 650w, https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-650.webp 650w, https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-650.jpeg 650w, https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of Node-RED's Manage Palette menu" alt="Screenshot of Node-RED's Manage Palette menu" loading="lazy" decoding="async" src="https://flowfuse.com/img/nr-manage-palette-cloudant-Tc-yCYJBZ1-650.jpeg" width="1300" height="753" /></picture></p>
<ol start="2">
<li><strong>FlowFuse Instance Settings:</strong> 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</li>
</ol>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-650.avif 650w, https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-650.webp 650w, https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-650.jpeg 650w, https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot of FlowFuse's 'Installed Modules' option in Instance > Settings > Palette" alt="Screenshot of FlowFuse's "Installed Modules" option in Instance > Settings > Palette" loading="lazy" decoding="async" src="https://flowfuse.com/img/ff-installed-modules-0p01ZkDzWv-650.jpeg" width="1300" height="753" /></picture></p>
<p>It's also easy to setup <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/">Environment Variables</a> for Node-RED in FlowFuse for when you integrate with external services like APIs too.</p>
<h2 id="security" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#security"></a> Security</h2>
<p>As with IBM Cloud, FlowFuse makes it very easy to secure your Node-RED Applications. <a href="https://flowfuse.com/docs/user/instance-settings/#security">FlowFuse offers three tiers of security</a> 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.</p>
<ul>
<li><strong>None</strong>: Anyone will be able to access the exposed routes.</li>
<li><strong>Basic Auth</strong>: Setup a single, dedicated username and password combination that is required in order to access the routes.</li>
<li><strong>FlowFuse Credentials</strong>: Visitors can use their FlowFuse username/password in order to access the endpoints. This also includes SSO if you have that configured for your FlowFoerge Team.</li>
</ul>
<h2 id="migrating-existing-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#migrating-existing-instances"></a> Migrating Existing Instances</h2>
<p>If you're looking to move your Node-RED applications from IBM Cloud, then you can do so through one of two options:</p>
<h3 id="node-red-tools-plugin" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#node-red-tools-plugin"></a> Node-RED Tools Plugin</h3>
<p>You can use our Node-RED Tools plugin to migrate your flows and credentials over to FlowFuse. You can read the details in our <a href="https://flowfuse.com/docs/migration/introduction/">Migration Guide</a>, which also includes instructions on how to export your environment variables too.</p>
<h3 id="manual-import" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/ibmcloud-starter-removed/#manual-import"></a> Manual Import</h3>
<p>This will only enable you to import your Flows, not the associated credentials.</p>
<ol>
<li>Export your existing <code>flows.json</code> from your IBM Cloud-hosted Node-RED instance by choosing "Export > All Flows > Download" within Node-RED</li>
<li>Create a new Application on FlowFuse</li>
<li>Once created, click the Node-RED instance that has been generated within your Application</li>
<li>Click "Settings"</li>
<li>Scroll down and select the "Import Instance" option</li>
<li>Choose your <code>flows.json</code> file that you downloaded earlier.</li>
</ol>
<p><em>If you have any questions about the above, or more generally about Node-RED or FlowFuse, then please do reach out and <a href="https://flowfuse.com/contact-us/">get in touch</a>.</em></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/Node-RED Tips - Importing, Exporting, and Grouping FlowsSave yourself time when working on Node-RED with these three tips.2023-03-27T12:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-copy-and-share-your-flows-using-export-and-import" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#1.-copy-and-share-your-flows-using-export-and-import"></a> 1. Copy and share your flows using Export and Import</h3>
<p>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.</p>
<p>To import a flow, follow these steps:</p>
<ol>
<li>Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on "Import".</li>
<li>Select the JSON file that contains the flow you want to import.</li>
<li>Click on "Import" and the flow will be imported into the current instance of Node-RED.</li>
</ol>
<p>To export a flow, follow these steps:</p>
<ol>
<li>Click on the three horizontal lines in the upper right corner of the Node-RED editor and click on "Export".</li>
<li>Select the type of export you want to perform - this can be either the "Clipboard" or "File".</li>
<li>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.</li>
<li>Use the exported file or clipboard content to import into a different instance of Node-RED.</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/import-export-0NdC7AzIu7-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Importing and exporting your flows" alt="Importing and exporting your flows" loading="lazy" decoding="async" src="https://flowfuse.com/img/import-export-0NdC7AzIu7-650.webp" width="650" height="315" /></picture></p>
<p>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.</p>
<h3 id="2.-import-helpful-example-flows-provided-with-custom-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#2.-import-helpful-example-flows-provided-with-custom-nodes"></a> 2. Import helpful example flows provided with custom nodes</h3>
<p>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.</p>
<p>To use example flows in custom nodes, follow these steps:</p>
<ol>
<li>Open the Node-RED editor and drag the custom node you want to use into your flow.</li>
<li>Double-click on the node to open its configuration panel.</li>
<li>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.</li>
<li>Click on the "Examples" button to bring up a list of example flows included with the node.</li>
<li>Select the example flow you want to use and click on "Import" to add the example flow to your Node-RED workspace.</li>
</ol>
<p>Once the example flow has been added to your workspace, you can modify it to fit your specific needs.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/example-lX-HGizzlx-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using the example flow included in the moment node" alt="Using the example flow included in the moment node" loading="lazy" decoding="async" src="https://flowfuse.com/img/example-lX-HGizzlx-650.webp" width="650" height="315" /></picture></p>
<p>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.</p>
<h3 id="3.-group-nodes-together-to-make-your-flows-easier-to-read" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#3.-group-nodes-together-to-make-your-flows-easier-to-read"></a> 3. Group nodes together to make your flows easier to read</h3>
<p>The group feature in Node-RED allows users to visually group nodes together within the workspace. This feature offers several benefits:</p>
<ol>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>Reduced clutter: Grouping nodes can help reduce the visual clutter in your workspace, making it easier to focus on key aspects of your flow.</p>
</li>
</ol>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/groups-edGmydKG4z-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Grouping your nodes to make them easier to read" alt="Grouping your nodes to make them easier to read" loading="lazy" decoding="async" src="https://flowfuse.com/img/groups-edGmydKG4z-650.webp" width="650" height="315" /></picture></p>
<p>Overall, the group feature in Node-RED is a valuable tool that can help users better organise, edit, and share their flows.</p>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
<p>You can read our previous Node-RED tips here.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/">Node-RED Tips - Smooth, Catch, and Math</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/">Node-RED Tips - Exec, Filter, and Debug</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/">Node-RED Tips - Deploying, Debugging, and Delaying</a><br />
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/">Node-RED Tips - Wiring Shortcuts
</a></p>
<h2 id="version-control-and-collaboration-for-your-node-red-flows" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-5/#version-control-and-collaboration-for-your-node-red-flows"></a> Version Control and Collaboration for Your Node-RED Flows</h2>
<p><a href="https://flowfuse.com/">FlowFuse</a> 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.</p>
<p>With FlowFuse <a href="https://flowfuse.com/docs/user/shared-library/#shared-team-library">Team Library</a>, 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.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> for a free trial today and discover how FlowFuse can enhance your Node</strong>*</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/The benefits and drawbacks of using Node-RED function nodesIn this blog post, I will discuss some of the benefits and drawbacks of using Function nodes in your next Node-RED project.2023-03-20T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h2 id="5-benefits-of-using-function-nodes%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/#5-benefits-of-using-function-nodes%3A"></a> 5 Benefits of using Function Nodes:</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/function-example-8Dq7hif9AK-642.gif 642w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example showing how to use the function node" alt="Example showing how to use the function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/function-example-8Dq7hif9AK-642.webp" width="642" height="394" /></picture></p>
<ol>
<li>
<p><strong>Customisation:</strong> 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.</p>
</li>
<li>
<p><strong>Reusability:</strong> 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.</p>
</li>
<li>
<p><strong>Debugging:</strong> 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.</p>
</li>
<li>
<p><strong>Performance:</strong> 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.</p>
</li>
<li>
<p><strong>Flexibility:</strong> 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.</p>
</li>
</ol>
<h2 id="5-benefits-of-avoiding-function-nodes%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/#5-benefits-of-avoiding-function-nodes%3A"></a> 5 Benefits of avoiding Function Nodes:</h2>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/no-function-example-z0eOdJ5sSc-642.gif 642w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example showing how to not use the function node" alt="Example showing how to not use the function node" loading="lazy" decoding="async" src="https://flowfuse.com/img/no-function-example-z0eOdJ5sSc-642.webp" width="642" height="394" /></picture></p>
<ol>
<li>
<p><strong>Simplicity:</strong> 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.</p>
</li>
<li>
<p><strong>Ease of Use:</strong> 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.</p>
</li>
<li>
<p><strong>Modularity:</strong> 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.</p>
</li>
<li>
<p><strong>Community Support:</strong> 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.</p>
</li>
<li>
<p><strong>Compatibility:</strong> Standard Node-RED nodes are usually compatible with all versions of Node-RED, making it easier to migrate flows between different environments.</p>
</li>
</ol>
<h2 id="how-to-easily-create-function-nodes-in-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/#how-to-easily-create-function-nodes-in-flowfuse"></a> How to Easily Create Function Nodes in FlowFuse</h2>
<p>FlowFuse offers a robust platform for building, scaling, and securing your Node-RED applications.</p>
<p>We are constantly adding new features to make it easy to use in the enterprise where you can rapidly improve your industrial processes. The <strong>"FlowFuse Assistant."</strong> 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.</p>
<p>For more details on using the FlowFuse Assistant, visit <a href="https://flowfuse.com/docs/user/assistant/">the Assistants Documentation</a>.</p>
<h2 id="conclusion%3A" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/why-should-you-use-node-red-function-nodes/#conclusion%3A"></a> Conclusion:</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/FlowFuse v1.5 Now AvailableUpdates to UI and architecture to allow for future features and Node-RED 3.1.0 Beta Available!2023-03-16T15:00:00ZJoe Pavitt<p>For FlowFuse 1.5 we have been busy making a lot of UX changes and upgrading our underlying architecture to enable future innovations on the FlowFuse platform.</p>
<!--more-->
<p>With our recently announced <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/terminology-changes/">Terminology Changes</a>, we have introduced some new concepts into FlowFuse.</p>
<ul>
<li><strong>Application</strong>: A group of Node-RED Instances Each instance can run locally (in FlowFuse) or remotely (on Devices)</li>
<li><strong>Instances</strong>: We renamed "Projects" to "Instances" to be more inline with the terminology used in the Node-RED community</li>
</ul>
<p>As such, our User Experience has been updated to reflect these changes, and allow for further functionality to be introduced with our plans for <a href="https://github.com/FlowFuse/flowfuse/issues/1689">Multiple Instances per Application</a>.</p>
<h3 id="%22applications%22-view" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#%22applications%22-view"></a> "Applications" View</h3>
<p>At the top-level in FlowFuse, you can now see a list of your "Appications". In FlowFuse 1.5, as we still have a 1:1 relationship of Applications to Local Instances, this will be the same as the list of "Projects" that you're used to seeing.</p>
<p></p>
<figcaption class="-mt-6 text-center"><b>"Applications" view in FlowFuse, listing all available Applications</b></figcaption>
<p>For 1.5, all of your settings, environment variables, etc. are all now at the "Instance" level. Applications will gain a lot more functionality in future releases.</p>
<h3 id="%22instances%22-view" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#%22instances%22-view"></a> "Instances" View</h3>
<p>When clicking on one of your Applications, you will see a list of Node-RED instances bound to that Application.</p>
<p></p>
<figcaption class="-mt-6 text-center"><b>A list of Instances contained within a single Application.</b></figcaption>
<p>Clicking on this Instance, will open up the "Instance" view, this is an exact replica of the "Project" view you'll be used to seeing in FlowFuse, and contains all of the same functionality:</p>
<p></p>
<figcaption class="-mt-6 text-center"><b>FlowFuse 1.5's "Instance" view. This contains all of the functionality previously found in the "Project" view.</b></figcaption>
<h3 id="devices-%26-managing-remote-instances" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#devices-%26-managing-remote-instances"></a> Devices & Managing Remote Instances</h3>
<p>Devices are now bound to "Instances", you'll see these in the "Devices" view, and can be managed and deployed to in exactly the same way as before. Devices will run whatever you've selected as your "Target Snapshot" for this Instance.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-650.avif 650w, https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-650.webp 650w, https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-650.jpeg 650w, https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot to show an Instance's 'Devices' view" alt=""Screenshot to show an Instance's 'Devices' view"" loading="lazy" decoding="async" src="https://flowfuse.com/img/screenshot-devices-b7n0SGxT_Z-650.jpeg" width="1300" height="752" /></picture></p>
<figcaption class="-mt-6 text-center"><b>"Devices" view, available for a given Node-RED Instance. This lists all of the connected devices to a given instance, that will automatically update when a new Target Snapshot is set.</b></figcaption>
<h2 id="node-red-3.1-beta-available" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#node-red-3.1-beta-available"></a> Node-RED 3.1 Beta Available</h2>
<p>FlowFuse Cloud is a great place to try out the new Node-RED features, with FlowFuse Cloud now including the <a href="https://discourse.nodered.org/t/node-red-3-1-0-beta-2-released/76192">Node-RED 3.1.0-beta.2</a>. If you want to try this version you can <a href="https://flowfuse.com/docs/user/instance-settings/#copy-instance">duplicate your application</a> or <a href="https://flowfuse.com/docs/user/changestack/">upgrade your stack</a>.</p>
<h2 id="other-improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#other-improvements"></a> Other Improvements</h2>
<ul>
<li>Update to audit logs to improve usability [<a href="https://github.com/FlowFuse/flowfuse/issues/1800">#1800</a>] [<a href="https://github.com/FlowFuse/flowfuse/issues/1785">#1785</a>]</li>
<li>Improve how licensing works with overages, for easier scaling of FlowFuse and your Node-RED Instances [<a href="https://github.com/FlowFuse/flowfuse/issues/1639">#1639</a>] [<a href="https://github.com/FlowFuse/flowfuse/issues/1739">#1739</a>]</li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#bug-fixes"></a> Bug Fixes</h2>
<ul>
<li>Device "Last Seen" status shows "never" even though it has previously been seen [<a href="https://github.com/FlowFuse/flowfuse/issues/1723">#1723</a>]</li>
<li>Improved Safe Mode launch for small projects [<a href="https://github.com/FlowFuse/flowfuse/issues/1579">#1579</a>]</li>
</ul>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.5.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That's also a great place to send us any feedback or feature requests.</p>
<p>You can also get help on <a href="https://discourse.nodered.org/">the Node-RED forums</a></p>
<p>As well as in the <a href="https://github.com/FlowFuse/flowfuse/discussions">forum within our Github project</a></p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a></p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/terminology-changes/Terminology ChangesApplications, Instances & Devices - the new way forward for FlowFuse2023-03-16T14:00:00ZJoe Pavitt<p>As a new product in the market, we constantly have to make choices on how to name things. Naming things is hard! As you name a thing, say "Project", it might be suitable now, but the product evolves, and may outgrow the name such that it doesn’t fit anymore.</p>
<!--more-->
<p>We are at this point with FlowFuse, and want to walk you through what we have planned, and why we are changing a couple of things.</p>
<h3 id="enter-the-%22application%22" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/terminology-changes/#enter-the-%22application%22"></a> Enter the "Application"</h3>
<p>In <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/flowforge-1-5-0-released/">FlowFuse 1.5</a>, we have introduced a new concept called an <strong>Application</strong>.</p>
<p>An Application will allow you to organize multiple Node-RED instances into a single managed group.</p>
<p>As of the 1.5 release, an Application can still only have a single Node-RED instance, but in future releases, Applications will allow for multiple Node-RED instances and will allow us to implement capabilities such as <strong>DevOps Pipelines</strong> and <strong>High Availability</strong>.</p>
<h3 id="%22projects%22-to-%22instances%22" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/terminology-changes/#%22projects%22-to-%22instances%22"></a> "Projects" to "Instances"</h3>
<p>Until now, 'Project' encapsulated both the Node-RED instance that was running in FlowFuse, and the associated devices (remote instances), settings and environment variables. It was an overloaded term, and it caused confusion with our users.</p>
<p>To simplify things, and to adhere more to the terminology familiar with the Node-RED community, we are renaming Projects to <strong>Instances</strong>.</p>
<p>An <strong>Instance</strong> is a customized version of Node-RED that includes various FlowFuse plugins to integrate it with the FlowFuse platform. It can also be used to manage the environment variables used in your Node-RED flows.</p>
<p>Instances can either be:</p>
<ul>
<li><strong>Local</strong> - An instance of Node-RED running in FlowFuse.</li>
<li><strong>Remote</strong> - An instance of Node-RED, managed by FlowFuse, running on a Device.</li>
</ul>
<p>In future releases, environment variables will also be able to be stored at the Application level, and shared across multiple Node-RED Instances.</p>
<h3 id="devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/terminology-changes/#devices"></a> Devices</h3>
<p>FlowFuse can also be used to manage remote Node-RED instances. This is typically useful when you have a number of remote devices that are required to run the same Node-RED instance, and may have variation in configuration or environment variables for example.</p>
<p>Devices are registered to an Instance, and can be configured to run <a href="https://flowfuse.com/docs/user/concepts/#instance-snapshot">Snapshots</a> of the Instance running in FlowFuse.</p>
<p>To accomplish this remote management capability, the <a href="https://github.com/FlowFuse/device-agent">FlowFuse Device Agent</a> needs to be installed on each device. Devices are registered with a Team, and then the appropriate device(s) are assigned to a Node-RED instance that should be deployed to the device(s). When the Node-RED instance is ready for deployment, a user creates a snapshot of the instance and marks it as a target snapshot for the device.</p>
<p>We hope these changes will simplify the FlowFuse terminology for our users and allow us to grow the FlowFuse platform. If you have any feedback or thoughts, please do reach out to us.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/Node-RED Tips - Smooth, Catch, and MathSave yourself time when working on Node-RED with these three tips.2023-03-13T12:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-use-the-smooth-node-to-get-the-minimum-and-maximum-values-of-your-payloads" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/#1.-use-the-smooth-node-to-get-the-minimum-and-maximum-values-of-your-payloads"></a> 1. Use the Smooth node to get the minimum and maximum values of your payloads</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/smooth-FvHv8Oic-h-503.avif 503w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/smooth-FvHv8Oic-h-503.webp 503w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using the Smooth node to return highest value from the last 100 payloads" alt="Using the Smooth node to return highest value from the last 100 payloads" loading="lazy" decoding="async" src="https://flowfuse.com/img/smooth-FvHv8Oic-h-503.jpeg" width="503" height="523" /></picture></p>
<p>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.</p>
<div style="position: relative" id="code-container-16">
<pre class="language-json"><code id="code-16" class="language-json"><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"9484c25a0120bd48"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"group"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Automatically outputs random value (temperature in Celcius) between 0 & 25 every second"</span><span class="token punctuation">,</span><span class="token property">"style"</span><span class="token operator">:</span><span class="token punctuation">{</span><span class="token property">"label"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token property">"nodes"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"e4f972f9daad6246"</span><span class="token punctuation">,</span><span class="token string">"c7fc075a1915e87b"</span><span class="token punctuation">,</span><span class="token string">"966a772c46dc2888"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">34</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">59</span><span class="token punctuation">,</span><span class="token property">"w"</span><span class="token operator">:</span><span class="token number">574</span><span class="token punctuation">,</span><span class="token property">"h"</span><span class="token operator">:</span><span class="token number">82</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"e4f972f9daad6246"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"link out"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"9484c25a0120bd48"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"link out 1"</span><span class="token punctuation">,</span><span class="token property">"mode"</span><span class="token operator">:</span><span class="token string">"link"</span><span class="token punctuation">,</span><span class="token property">"links"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"33594c64783cdc45"</span><span class="token punctuation">,</span><span class="token string">"e6bf7b494b48861e"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">355</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">100</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"c7fc075a1915e87b"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"inject"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"9484c25a0120bd48"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"props"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"repeat"</span><span class="token operator">:</span><span class="token string">"1"</span><span class="token punctuation">,</span><span class="token property">"crontab"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"once"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"onceDelay"</span><span class="token operator">:</span><span class="token number">0.1</span><span class="token punctuation">,</span><span class="token property">"topic"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">130</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">100</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"966a772c46dc2888"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"966a772c46dc2888"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"random"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"9484c25a0120bd48"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"low"</span><span class="token operator">:</span><span class="token string">"0"</span><span class="token punctuation">,</span><span class="token property">"high"</span><span class="token operator">:</span><span class="token string">"25"</span><span class="token punctuation">,</span><span class="token property">"inte"</span><span class="token operator">:</span><span class="token string">"true"</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">260</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">100</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"e4f972f9daad6246"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"group"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Calculate average, high and low, save to flow"</span><span class="token punctuation">,</span><span class="token property">"style"</span><span class="token operator">:</span><span class="token punctuation">{</span><span class="token property">"label"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token property">"nodes"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"cc3978c7c4ea56ed"</span><span class="token punctuation">,</span><span class="token string">"e1819526a5f365c8"</span><span class="token punctuation">,</span><span class="token string">"33594c64783cdc45"</span><span class="token punctuation">,</span><span class="token string">"fea261a15b3b7683"</span><span class="token punctuation">,</span><span class="token string">"e28a69232f1cac53"</span><span class="token punctuation">,</span><span class="token string">"aecb1727be523240"</span><span class="token punctuation">,</span><span class="token string">"e30039ee13e480a8"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">34</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">259</span><span class="token punctuation">,</span><span class="token property">"w"</span><span class="token operator">:</span><span class="token number">552</span><span class="token punctuation">,</span><span class="token property">"h"</span><span class="token operator">:</span><span class="token number">142</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"cc3978c7c4ea56ed"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"change"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"rules"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"t"</span><span class="token operator">:</span><span class="token string">"set"</span><span class="token punctuation">,</span><span class="token property">"p"</span><span class="token operator">:</span><span class="token string">"high"</span><span class="token punctuation">,</span><span class="token property">"pt"</span><span class="token operator">:</span><span class="token string">"flow"</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"tot"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"action"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"from"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"reg"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">310</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">300</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"aecb1727be523240"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"e1819526a5f365c8"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"change"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"rules"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"t"</span><span class="token operator">:</span><span class="token string">"set"</span><span class="token punctuation">,</span><span class="token property">"p"</span><span class="token operator">:</span><span class="token string">"low"</span><span class="token punctuation">,</span><span class="token property">"pt"</span><span class="token operator">:</span><span class="token string">"flow"</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"tot"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"action"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"from"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"reg"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">310</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">360</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"e30039ee13e480a8"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"33594c64783cdc45"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"link in"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"link in 1"</span><span class="token punctuation">,</span><span class="token property">"links"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"c07b2e101cecbd3b"</span><span class="token punctuation">,</span><span class="token string">"e4f972f9daad6246"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">75</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">320</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"e28a69232f1cac53"</span><span class="token punctuation">,</span><span class="token string">"fea261a15b3b7683"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"fea261a15b3b7683"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"smooth"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Min"</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"action"</span><span class="token operator">:</span><span class="token string">"min"</span><span class="token punctuation">,</span><span class="token property">"count"</span><span class="token operator">:</span><span class="token string">"100"</span><span class="token punctuation">,</span><span class="token property">"round"</span><span class="token operator">:</span><span class="token string">"2"</span><span class="token punctuation">,</span><span class="token property">"mult"</span><span class="token operator">:</span><span class="token string">"single"</span><span class="token punctuation">,</span><span class="token property">"reduce"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">170</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">360</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"e1819526a5f365c8"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"e28a69232f1cac53"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"smooth"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Max"</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"action"</span><span class="token operator">:</span><span class="token string">"max"</span><span class="token punctuation">,</span><span class="token property">"count"</span><span class="token operator">:</span><span class="token string">"100"</span><span class="token punctuation">,</span><span class="token property">"round"</span><span class="token operator">:</span><span class="token string">"2"</span><span class="token punctuation">,</span><span class="token property">"mult"</span><span class="token operator">:</span><span class="token string">"single"</span><span class="token punctuation">,</span><span class="token property">"reduce"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">170</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">300</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"cc3978c7c4ea56ed"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"aecb1727be523240"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"debug"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"debug 23"</span><span class="token punctuation">,</span><span class="token property">"active"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"tosidebar"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"console"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tostatus"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"complete"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"targetType"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">,</span><span class="token property">"statusVal"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"statusType"</span><span class="token operator">:</span><span class="token string">"auto"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">480</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">300</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"e30039ee13e480a8"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"debug"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"37380f26e8bfc98a"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"debug 25"</span><span class="token punctuation">,</span><span class="token property">"active"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"tosidebar"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"console"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tostatus"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"complete"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"targetType"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">,</span><span class="token property">"statusVal"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"statusType"</span><span class="token operator">:</span><span class="token string">"auto"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">480</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">360</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"group"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"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"</span><span class="token punctuation">,</span><span class="token property">"style"</span><span class="token operator">:</span><span class="token punctuation">{</span><span class="token property">"label"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token property">"nodes"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"9393c22b2e3c1ec8"</span><span class="token punctuation">,</span><span class="token string">"1cd18307cb919159"</span><span class="token punctuation">,</span><span class="token string">"7c1f474e646765a7"</span><span class="token punctuation">,</span><span class="token string">"a209cd10f33ec401"</span><span class="token punctuation">,</span><span class="token string">"f4b15283e55babf4"</span><span class="token punctuation">,</span><span class="token string">"e6bf7b494b48861e"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">34</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">419</span><span class="token punctuation">,</span><span class="token property">"w"</span><span class="token operator">:</span><span class="token number">1032</span><span class="token punctuation">,</span><span class="token property">"h"</span><span class="token operator">:</span><span class="token number">162</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"9393c22b2e3c1ec8"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"switch"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Was the value between flow.high and flow.low?"</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"propertyType"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">,</span><span class="token property">"rules"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"t"</span><span class="token operator">:</span><span class="token string">"btwn"</span><span class="token punctuation">,</span><span class="token property">"v"</span><span class="token operator">:</span><span class="token string">"high"</span><span class="token punctuation">,</span><span class="token property">"vt"</span><span class="token operator">:</span><span class="token string">"flow"</span><span class="token punctuation">,</span><span class="token property">"v2"</span><span class="token operator">:</span><span class="token string">"low"</span><span class="token punctuation">,</span><span class="token property">"v2t"</span><span class="token operator">:</span><span class="token string">"flow"</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"t"</span><span class="token operator">:</span><span class="token string">"else"</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"checkall"</span><span class="token operator">:</span><span class="token string">"true"</span><span class="token punctuation">,</span><span class="token property">"repair"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"outputs"</span><span class="token operator">:</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">300</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">480</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"1cd18307cb919159"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">"7c1f474e646765a7"</span><span class="token punctuation">,</span><span class="token string">"a209cd10f33ec401"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"1cd18307cb919159"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"debug"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"debug 15"</span><span class="token punctuation">,</span><span class="token property">"active"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"tosidebar"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"console"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tostatus"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"complete"</span><span class="token operator">:</span><span class="token string">"false"</span><span class="token punctuation">,</span><span class="token property">"statusVal"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"statusType"</span><span class="token operator">:</span><span class="token string">"auto"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">560</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">460</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"7c1f474e646765a7"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"debug"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"debug 16"</span><span class="token punctuation">,</span><span class="token property">"active"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tosidebar"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"console"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tostatus"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"complete"</span><span class="token operator">:</span><span class="token string">"false"</span><span class="token punctuation">,</span><span class="token property">"statusVal"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"statusType"</span><span class="token operator">:</span><span class="token string">"auto"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">560</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">540</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"a209cd10f33ec401"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"change"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Alert to debug when value is outside of the range"</span><span class="token punctuation">,</span><span class="token property">"rules"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"t"</span><span class="token operator">:</span><span class="token string">"set"</span><span class="token punctuation">,</span><span class="token property">"p"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">,</span><span class="token property">"pt"</span><span class="token operator">:</span><span class="token string">"msg"</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">"The value was outside of the range"</span><span class="token punctuation">,</span><span class="token property">"tot"</span><span class="token operator">:</span><span class="token string">"str"</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"action"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"property"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"from"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"to"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"reg"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">690</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">500</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"f4b15283e55babf4"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"f4b15283e55babf4"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"debug"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"debug 17"</span><span class="token punctuation">,</span><span class="token property">"active"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"tosidebar"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">,</span><span class="token property">"console"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"tostatus"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"complete"</span><span class="token operator">:</span><span class="token string">"false"</span><span class="token punctuation">,</span><span class="token property">"statusVal"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"statusType"</span><span class="token operator">:</span><span class="token string">"auto"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">960</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">500</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"e6bf7b494b48861e"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"link in"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"ec11a9ee9148b0b5"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"link in 2"</span><span class="token punctuation">,</span><span class="token property">"links"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"e4f972f9daad6246"</span><span class="token punctuation">,</span><span class="token string">"c07b2e101cecbd3b"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">75</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">480</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"9393c22b2e3c1ec8"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"caf4214602d5f2c9"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"group"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"Manually send a spurious value"</span><span class="token punctuation">,</span><span class="token property">"style"</span><span class="token operator">:</span><span class="token punctuation">{</span><span class="token property">"label"</span><span class="token operator">:</span>!<span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token property">"nodes"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"14097fb7ba3a9ecc"</span><span class="token punctuation">,</span><span class="token string">"c07b2e101cecbd3b"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">34</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">159</span><span class="token punctuation">,</span><span class="token property">"w"</span><span class="token operator">:</span><span class="token number">232</span><span class="token punctuation">,</span><span class="token property">"h"</span><span class="token operator">:</span><span class="token number">82</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"14097fb7ba3a9ecc"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"inject"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"caf4214602d5f2c9"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"props"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">{</span><span class="token property">"p"</span><span class="token operator">:</span><span class="token string">"payload"</span><span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"repeat"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"crontab"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"once"</span><span class="token operator">:</span>!<span class="token number">1</span><span class="token punctuation">,</span><span class="token property">"onceDelay"</span><span class="token operator">:</span><span class="token number">0.1</span><span class="token punctuation">,</span><span class="token property">"topic"</span><span class="token operator">:</span><span class="token string">""</span><span class="token punctuation">,</span><span class="token property">"payload"</span><span class="token operator">:</span><span class="token string">"75"</span><span class="token punctuation">,</span><span class="token property">"payloadType"</span><span class="token operator">:</span><span class="token string">"num"</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">130</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">200</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"c07b2e101cecbd3b"</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">{</span><span class="token property">"id"</span><span class="token operator">:</span><span class="token string">"c07b2e101cecbd3b"</span><span class="token punctuation">,</span><span class="token property">"type"</span><span class="token operator">:</span><span class="token string">"link out"</span><span class="token punctuation">,</span><span class="token property">"z"</span><span class="token operator">:</span><span class="token string">"dd95c0bca1101c86"</span><span class="token punctuation">,</span><span class="token property">"g"</span><span class="token operator">:</span><span class="token string">"caf4214602d5f2c9"</span><span class="token punctuation">,</span><span class="token property">"name"</span><span class="token operator">:</span><span class="token string">"link out 2"</span><span class="token punctuation">,</span><span class="token property">"mode"</span><span class="token operator">:</span><span class="token string">"link"</span><span class="token punctuation">,</span><span class="token property">"links"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token string">"33594c64783cdc45"</span><span class="token punctuation">,</span><span class="token string">"e6bf7b494b48861e"</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token property">"x"</span><span class="token operator">:</span><span class="token number">225</span><span class="token punctuation">,</span><span class="token property">"y"</span><span class="token operator">:</span><span class="token number">200</span><span class="token punctuation">,</span><span class="token property">"wires"</span><span class="token operator">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token punctuation">]</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-16" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<h3 id="2.-perform-simple-maths-functions-using-jsonata-in-change-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/#2.-perform-simple-maths-functions-using-jsonata-in-change-nodes"></a> 2. Perform simple maths functions using JSONata in Change nodes</h3>
<p>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 <a href="https://flows.nodered.org/node/node-red-contrib-calc">node-red-contrib-calc</a> but you can also easily complete the same task within a change node.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/jsonata-Mjsu67V8r0-503.avif 503w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/jsonata-Mjsu67V8r0-503.webp 503w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using JSONata in a Change node to multiply a payload by 2.5" alt="Using JSONata in a Change node to multiply a payload by 2.5" loading="lazy" decoding="async" src="https://flowfuse.com/img/jsonata-Mjsu67V8r0-503.jpeg" width="503" height="314" /></picture></p>
<p>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.</p>
<div id="nr-flow-108" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow108 = "\n[{\"id\":\"6bbe9c1e81c4ee39\",\"type\":\"inject\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"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\":[{\"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\":[]},{\"id\":\"1c04633997beb150\",\"type\":\"inject\",\"z\":\"cfe9fec308e144db\",\"name\":\"\",\"props\":[{\"p\":\"payload\"}],\"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\":[{\"p\":\"payload\"}],\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"4\",\"payloadType\":\"num\",\"x\":350,\"y\":480,\"wires\":[[\"07aa636f3db17775\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow108.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-108') })</script>
<h3 id="3.-use-the-catch-node-to-trigger-flows-on-errors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/#3.-use-the-catch-node-to-trigger-flows-on-errors"></a> 3. Use the Catch node to trigger flows on errors</h3>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/catch-QA1YW4GT_n-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Catching an error from the Read File node and outputting a message to debug" alt="Catching an error from the Read File node and outputting a message to debug" loading="lazy" decoding="async" src="https://flowfuse.com/img/catch-QA1YW4GT_n-650.webp" width="650" height="181" /></picture></p>
<p>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.</p>
<div id="nr-flow-109" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow109 = "\n[{\"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\":[]},{\"id\":\"de70fda720070c57\",\"type\":\"inject\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"\",\"props\":[],\"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\":[\"a18f9c8638c78e57\"],\"uncaught\":false,\"x\":790,\"y\":300,\"wires\":[[\"b22988df6357a52a\"]]},{\"id\":\"b22988df6357a52a\",\"type\":\"change\",\"z\":\"0c6a2ba248b5933f\",\"name\":\"Debug message\",\"rules\":[{\"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\"]]}]\n";
new FlowRenderer().renderFlows(JSON.parse(flow109.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-109') })</script>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
<h3 id="simplifying-multi-instance-communication-with-flowfuse-project-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-4/#simplifying-multi-instance-communication-with-flowfuse-project-nodes"></a> Simplifying Multi-Instance Communication with FlowFuse Project Nodes</h3>
<p>Coordinating communication between multiple Node-RED instances can be challenging, but FlowFuse's <a href="https://flowfuse.com/docs/user/projectnodes/">Project Nodes</a> make it effortless. With these nodes, you can seamlessly send messages between instances without dealing with complicated network configurations.</p>
<p>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.</p>
<p>FlowFuse continues to push the boundaries of collaboration and scalability in Node-RED projects. For more details on these features, visit the <a href="https://flowfuse.com/">FlowFuse website</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/Comparing Node-RED Dashboards SolutionsOne of the most common features Node-RED users add to their flows is a dashboard, we compaure the 3 most popular options.2023-03-13T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<div class="blog-update-notes">
<p><strong>UPDATE:</strong> Since this article was published, it's worth noting a couple of important updates:</p>
<ul>
<li>FlexDash is no longer maintained and supported.</li>
<li><a href="https://dashboard.flowfuse.com/">Node-RED Dashboard 2.0</a> 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.</li>
</ul>
</div>
<h2 id="which-dashboards-am-i-going-to-consider%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#which-dashboards-am-i-going-to-consider%3F"></a> Which dashboards am I going to consider?</h2>
<p>Based on their downloads per week and active development, I believe there are 3 main dashboards worth considering. In no particular order, they are <a href="https://flows.nodered.org/node/node-red-dashboard">Dashboard</a>, <a href="https://flows.nodered.org/node/node-red-contrib-uibuilder">uibuilder</a>, and <a href="https://flows.nodered.org/node/@flexdash/node-red-fd-corewidgets">FlexDash</a>.</p>
<p>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.</p>
<p>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 <a href="https://github.com/dceejay">Dave</a>, <a href="https://github.com/TotallyInformation">Julian</a>, and <a href="https://github.com/tve">Thorsten</a> for their replies as well as all the work they've put into these great projects!</p>
<h2 id="methodology" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#methodology"></a> Methodology</h2>
<p>To compare these dashboards, I am going to consider each of them based on the following factors:</p>
<ul>
<li>How easy is it to install?</li>
<li>How easy is it to get your first demo dashboard running?</li>
<li>How extensive is the collection of UI elements?</li>
<li>How good is the support and documentation?</li>
<li>How 'cloud native' is the dashboard?</li>
<li>How active is each project's development?</li>
<li>What are the future development plans?</li>
</ul>
<p>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.</p>
<p>So, that's the methodology, let's get on with looking at the strengths of each project.</p>
<h2 id="how-easy-is-it-to-install%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-easy-is-it-to-install%3F"></a> How easy is it to install?</h2>
<h3 id="uibuilder---1st-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---1st-place"></a> uibuilder - 1st place</h3>
<p>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.</p>
<h3 id="dashboard---2nd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#dashboard---2nd-place"></a> Dashboard - 2nd place</h3>
<p>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.</p>
<h3 id="flexdash---3rd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash---3rd-place"></a> FlexDash - 3rd place</h3>
<p>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 <em>'You most likely do not want to explicitly install this package, you want to install the <a href="https://github.com/flexdash/node-red-fd-corewidgets">core widgets</a>, which will bring in this package and more and will provide a usable whole'.</em></p>
<p>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.</p>
<p>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.</p>
<p>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 <a href="https://flexdash.github.io/docs/quick-start/#installing-flexdash-in-node-red">covered in the docs</a> 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.</p>
<p>There is also an <a href="https://github.com/node-red/node-red/issues/569">ongoing discussion</a> about a way to resolve issue by changing how Node-RED deals with dependencies which sounds promising.</p>
<p>I believe a few improvements to the install process could make FlexDash a much more popular custom node.</p>
<h2 id="how-easy-is-it-to-get-your-first-demo-dashboard-running%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-easy-is-it-to-get-your-first-demo-dashboard-running%3F"></a> How easy is it to get your first demo dashboard running?</h2>
<h3 id="flexdash---1st-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash---1st-place"></a> FlexDash - 1st place</h3>
<p>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 /flexdash to the end of the URL of your Node-RED editor and you should have your first dashboard running.</p>
<h3 id="uibuilder---2nd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---2nd-place"></a> uibuilder - 2nd place</h3>
<p>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.</p>
<h3 id="dashboard---3rd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#dashboard---3rd-place"></a> Dashboard - 3rd place</h3>
<p>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.</p>
<p>You then need to deploy your flow and visit the dashboard using '/ui' on the end of the URL of your Node-RED editor and you are up and running.</p>
<p>Dashboard would benefit from some example flows as we see with the other two custom nodes.</p>
<h2 id="how-extensive-is-the-collection-of-ui-elements%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-extensive-is-the-collection-of-ui-elements%3F"></a> How extensive is the collection of UI elements?</h2>
<h3 id="flexdash-and-dashboard---joint-1st-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash-and-dashboard---joint-1st-place"></a> FlexDash and Dashboard - joint 1st place</h3>
<p>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.</p>
<h3 id="uibuilder---3rd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---3rd-place"></a> uibuilder - 3rd place</h3>
<p>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.</p>
<h2 id="how-good-is-the-support-and-documentation%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-good-is-the-support-and-documentation%3F"></a> How good is the support and documentation?</h2>
<h3 id="all-three---joint-first-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#all-three---joint-first-place"></a> All three - joint first place</h3>
<p>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.</p>
<h2 id="how-'cloud-native'-is-the-dashboard%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-'cloud-native'-is-the-dashboard%3F"></a> How 'cloud native' is the dashboard?</h2>
<p>In the words of the <a href="https://www.cncf.io/">Cloud Native Computing Foundation</a> '<em>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</em>'.</p>
<p>'<em>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</em>'.</p>
<p>So, how well does each project conform to these ideals?</p>
<h3 id="dashboard---1st-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#dashboard---1st-place"></a> Dashboard - 1st place</h3>
<p>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.</p>
<h3 id="flexdash---2nd-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash---2nd-place"></a> FlexDash - 2nd place</h3>
<p>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 <a href="https://github.com/node-red/node-red/issues/569">ongoing discussion</a> about a way to resolve issue by changing how Node-RED deals with dependencies.</p>
<h3 id="uibuilder---3rd-place-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---3rd-place-1"></a> uibuilder - 3rd place</h3>
<p>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.</p>
<h2 id="how-active-is-each-project's-development%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#how-active-is-each-project's-development%3F"></a> How active is each project's development?</h2>
<h3 id="uibuilder---1st-place-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---1st-place-1"></a> uibuilder - 1st place</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/uibuilder-activity-cLKzg3wBTI-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/uibuilder-activity-cLKzg3wBTI-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the uibuilder Github Commits" alt="Image showing the uibuilder Github Commits" loading="lazy" decoding="async" src="https://flowfuse.com/img/uibuilder-activity-cLKzg3wBTI-650.jpeg" width="650" height="169" /></picture></p>
<p>uibuilder has has consistent commits to the project going back several years with even greater activity since the start of 2022.</p>
<h3 id="flexdash---2nd-place-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash---2nd-place-1"></a> FlexDash - 2nd place</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flexdash-activity-0XsLHXnz45-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flexdash-activity-0XsLHXnz45-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the FlexDash Github Commits" alt="Image showing the FlexDash Github Commits" loading="lazy" decoding="async" src="https://flowfuse.com/img/flexdash-activity-0XsLHXnz45-650.jpeg" width="650" height="168" /></picture></p>
<p>The commits to FlexDash have been regular since mid 2022.</p>
<h3 id="dashboard---3rd-place-1" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#dashboard---3rd-place-1"></a> Dashboard - 3rd place</h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/dashboard-activity-royMlOREn_-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/dashboard-activity-royMlOREn_-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the Dashboard Github Commits" alt="Image showing the Dashboard Github Commits" loading="lazy" decoding="async" src="https://flowfuse.com/img/dashboard-activity-royMlOREn_-650.jpeg" width="650" height="170" /></picture></p>
<p>Dashboard is now in a a maintenance only state. In the words of the project lead, <em>'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'</em>.</p>
<p>In practice, this means that sooner or later using Dashboard might become a significant security risk.</p>
<h2 id="what-are-the-future-development-plans%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#what-are-the-future-development-plans%3F"></a> What are the future development plans?</h2>
<h3 id="uibuilder---joint-firstplace" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#uibuilder---joint-firstplace"></a> uibuilder - joint firstplace</h3>
<p>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, '<em>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</em>.</p>
<p><em>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</em>'.</p>
<p>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 <a href="https://totallyinformation.github.io/node-red-contrib-uibuilder/#/roadmap">development roadmap for uibuilder</a> the project should progress towards being easier and easier for low-coders to make use of.</p>
<p>To again quote the project lead, '<em>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</em>'.</p>
<h3 id="flexdash---joint-firstplace" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#flexdash---joint-firstplace"></a> FlexDash - joint firstplace</h3>
<p>The project Lead for FlexDash had the following to say about the future development of the project. '<em>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</em>'.</p>
<p>'<em>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</em>'.</p>
<p>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.</p>
<h3 id="dashboard---third-place" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#dashboard---third-place"></a> Dashboard - third place</h3>
<p><em><strong>Important Update: New Generation of Node-RED Dashboard Released:</strong></em> <em>A new generation of the outdated and unmaintained Node-RED Dashboard has been released to replace it. Introducing <a href="https://dashboard.flowfuse.com/">Node-RED Dashboard 2.0</a>, 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.</em></p>
<p>*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 <a href="https://dashboard.flowfuse.com/user/migration.html">Migration Guide</a>.</p>
<p>Dashboard 2.0 includes various chart types such as line, scatter, bar, gauge, and more, and it's compatible with all the <a href="https://vuetifyjs.com/en/components/all/#containment">Vuetify component library</a>, making it easier to build advanced dashboards.*</p>
<p><em>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/dashboard/">Node-RED Dashboard 2.0 Guides</a> for more information.</em></p>
<p>As mentioned above, Dashboard is no longer in active development. This is due to the framework upon which it was build <a href="https://angularjs.org/">(AngularJS)</a> 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 <a href="https://discourse.nodered.org/t/discussion-about-a-new-dashboard/51119/3">thread on the Node-RED forums</a>.</p>
<p>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.</p>
<h2 id="conclusions" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/comparing-node-red-dashboards/#conclusions"></a> Conclusions</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/Node-RED Tips - Exec, Filter, and DebugSave yourself time when working on Node-RED with these three tips.2023-03-07T12:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-the-exec-node-allows-you-to-interact-with-bash-from-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/#1.-the-exec-node-allows-you-to-interact-with-bash-from-node-red"></a> 1. The Exec node allows you to interact with BASH from Node-RED</h3>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/exec-example-hv78c4k81r-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Example flow using the Exec node" alt=""Example flow using the Exec node"" loading="lazy" decoding="async" src="https://flowfuse.com/img/exec-example-hv78c4k81r-650.webp" width="650" height="176" /></picture></p>
<h3 id="2.-the-filter-node-helps-you-discard-duplicate-messages" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/#2.-the-filter-node-helps-you-discard-duplicate-messages"></a> 2. The Filter node helps you discard duplicate messages</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/filter-config-ik5kNh_YWD-497.avif 497w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/filter-config-ik5kNh_YWD-497.webp 497w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Configuring the Filter node to only allow unique payloads through" alt="Configuring the Filter node to only allow unique payloads through" loading="lazy" decoding="async" src="https://flowfuse.com/img/filter-config-ik5kNh_YWD-497.jpeg" width="497" height="264" /></picture></p>
<p>Once your filter is configured as shown above, try sending different payloads through to see the outcome.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/filter-example-IA4SrkFU78-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Demonstration showing the Filter node" alt="Demonstration showing the Filter node" loading="lazy" decoding="async" src="https://flowfuse.com/img/filter-example-IA4SrkFU78-650.webp" width="650" height="157" /></picture></p>
<h3 id="3.-counting-the-amount-of-messages-sent-to-a-debug-node" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/#3.-counting-the-amount-of-messages-sent-to-a-debug-node"></a> 3. Counting the amount of messages sent to a Debug node</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/setup-counting-debug-FXnxDSSMHK-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/setup-counting-debug-FXnxDSSMHK-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Setting up the debug to count messages" alt="Setting up the debug to count messages" loading="lazy" decoding="async" src="https://flowfuse.com/img/setup-counting-debug-FXnxDSSMHK-650.jpeg" width="650" height="379" /></picture></p>
<p>Once you've setup the node as shown above, you will see a counter under the debug.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/counting-debug-Sp5DxbV6uM-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Each message sent to the debug node is counted" alt="Each message sent to the debug node is counted" loading="lazy" decoding="async" src="https://flowfuse.com/img/counting-debug-Sp5DxbV6uM-650.webp" width="650" height="269" /></picture></p>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
<h2 id="effortless-communication-between-node-red-instances-with-flowfuse-project-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/3-quick-node-red-tips-3/#effortless-communication-between-node-red-instances-with-flowfuse-project-nodes"></a> Effortless Communication Between Node-RED Instances with FlowFuse Project Nodes</h2>
<p>Managing communication between multiple Node-RED instances can be a complex task, but FlowFuse <a href="https://flowfuse.com/docs/user/projectnodes/">Project Nodes</a> 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.</p>
<p>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.</p>
<p>FlowFuse continues to innovate, making collaboration and scalability in Node-RED projects even easier. To learn more about these features, check out the <a href="https://flowfuse.com/">FlowFuse website</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/Node-RED: The Integration Platform for IIoT Edge Computing & PLCsNode-RED's Role in IIoT Edge Computing & PLC Integration2023-03-06T00:00:00Z<p>Node-RED has become a widely adopted integration platform for IoT edge computing and PLCs. Discover why!</p>
<!--more-->
<h2 id="the-integration-platform-for-iiot-edge-computing-%26-plcs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#the-integration-platform-for-iiot-edge-computing-%26-plcs"></a> The Integration Platform for IIoT Edge Computing & PLCs</h2>
<p>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, <a href="https://flowfuse.com/node-red/">Node-RED</a> makes it possible for software developers and non-professional software developers to create sophisticated applications.</p>
<p>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.</p>
<h2 id="how-node-red-fits-into-iiot-edge-computing" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#how-node-red-fits-into-iiot-edge-computing"></a> How Node-RED fits into IIoT Edge Computing</h2>
<p>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 <a href="https://flows.nodered.org/">thousands of nodes and flow</a>s to support a wider range of these sources of data, including support for <a href="https://flows.nodered.org/node/node-red-contrib-modbus">Modbus</a>, <a href="https://flows.nodered.org/node/node-red-contrib-opcua">OPC-UA</a>, <a href="https://flows.nodered.org/node/node-red-contrib-s7">S7</a>, <a href="https://cookbook.nodered.org/mqtt/">MQTT</a>, etc. Node-RED also has nodes for graphics and dashboards to make it trivial to visualize industrial data.</p>
<p>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.</p>
<h2 id="plc-and-iot-gateway-vendors-embrace-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#plc-and-iot-gateway-vendors-embrace-node-red"></a> PLC and IoT Gateway Vendors Embrace Node-RED</h2>
<p>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.</p>
<p>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.</p>
<h2 id="the-standard-for-edge-computing-and-plcs" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#the-standard-for-edge-computing-and-plcs"></a> The Standard for Edge Computing and PLCs</h2>
<p>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.</p>
<p>Below is a sample of the vendors offering a Node-RED solution:</p>
<ol>
<li><a href="https://www.advantech.com/en-eu/products/node-red-gateways/sub_fb7246cc-cc10-486f-806b-30bb50a90f28">Advantech</a> Node-RED Field Gateway</li>
<li><a href="https://infosys.beckhoff.com/english.php?content=../content/1033/tf6720_tc3_iot_data_agent/3260672139.html&id=">Bechhoff</a> TwinCAT</li>
<li><a href="https://www.bivocom.com/products/iot-gateways/edge-iot-gateway-tg452">Bivocom</a> TG452 IoT Edge Gateway</li>
<li><a href="https://www.bliiot.com/edge-computing-gateway-p00359p1.html">BLIIOT Edge Computing Gateway</a> EdgeCom BL302</li>
<li><a href="https://developer.community.boschrexroth.com/t5/Store-and-How-to/ctrlX-CORE-Node-RED-App/ba-p/22366">Bosch CtrlX Core</a></li>
<li><a href="https://www.broadsens.com/wireless-gateway/">Broadsens</a> GU200 & GU 200S</li>
<li><a href="https://www.emerson.com/documents/automation/product-datasheet-pacedge-software-computing-devices-pacsystems-en-7205588.pdf">Emerson</a> PACEdge</li>
<li><a href="https://github.com/HilscherAutomation/netPI-nodered">Hilscher Automation</a></li>
<li><a href="https://developer.opto22.com/nodered/general/">Opto22</a> groov RIO & EPIC</li>
<li><a href="https://www.parallaxav.com/controlsystem/">Parallax AV</a> Control System</li>
<li><a href="https://docs.particle.io/reference/cloud-apis/node-red/">Particle.io</a> Particle</li>
<li><a href="https://www.pepperl-fuchs.com/usa/en/classid_199.htm?view=productdetails&prodid=93839">Pepperl+Fuchs</a> AS-Interface gateway</li>
<li><a href="https://projects.raspberrypi.org/en/projects/getting-started-with-node-red">Raspberry Pi</a></li>
<li><a href="https://www.renesas.com/us/en/products/programmable-mixed-signal-asic-ip-products/mixed-signal-asics/communication-asics/ftclick-mikrobus-compatible-interface-module">Renesas</a> FT Click</li>
<li><a href="https://revolutionpi.com/revpi-connect/">Revolution Pi</a> RevPi Connect</li>
<li><a href="https://shop.exchange.se.com/en-US/apps/59823/ecostruxure-plant-data-expert/features">Schneider Electric</a> ExoStructure Plant Data Expert</li>
<li><a href="https://github.com/SIMATICmeetsLinux/IOT2050-NodeRed-OPCUA-Server">Siemens</a> S7 PLC</li>
<li><a href="https://st-one.io/en/">ST-One</a></li>
<li><a href="https://support.tulip.co/docs/using-node-red-with-edge-mc">Tulip</a> Edge MC & Edge IO</li>
<li><a href="https://www.wago.com/us/edge-devices">Wago</a> Edge Controller & Computer</li>
<li><a href="https://catalog.weidmueller.com/procat/Group.jsp;jsessionid=C885C404E7B4B798B23B8A9BB2200513?groupId=(%22group14048963834797%22)&page=Group">Weidmueller</a> control web</li>
</ol>
<p>There are several key reason Node-RED is so popular for IIoT edge computing, including:</p>
<ul>
<li>Easy User-friendly interface that makes it accessible to manufacturing engineers that might not have a lot of programming experience.</li>
<li>Large community of open source nodes that integrate with many different OT hardware and protocols.</li>
<li>Open source community and license making it vendor neutral so competing hardware vendors feel comfortable embracing the platform.</li>
</ul>
<h2 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/integration-platform-for-edge-computing/#conclusion"></a> Conclusion</h2>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/Community News March 2023Your monthly update for the FlowFuse and Node-RED communities2023-03-02T00:00:00Z<p>Welcome to the FlowFuse newsletter for March 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<h2 id="upcoming-events" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#upcoming-events"></a> Upcoming events</h2>
<h3 id="node-red-ask-me-anything" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#node-red-ask-me-anything"></a> Node-RED Ask Me Anything</h3>
<p>Back by popular demand, FlowFuse is hosting a monthly Node-RED Ask Me Anything session on March 9th. This is a great opportunity to ask Nick O’Leary, co-creator of Node-RED & FlowFuse CTO, and Rob Marcer, Node-RED FlowFuse Developer Educator your questions about Node-RED. <a href="https://flowfuse.com/ask-me-anything/ama-nodered/">Sign-up today to participate</a>.</p>
<h3 id="devops-for-node-red%3A-an-introduction-to-flowfuse-webinar" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#devops-for-node-red%3A-an-introduction-to-flowfuse-webinar"></a> DevOps for Node-RED: An Introduction to FlowFuse Webinar</h3>
<p>Join Nick O'Leary, FlowFuse CTO, as he presents an Introduction to FlowFuse and demonstrates FlowFuse’s platform for providing DevOps for Node-RED. This webinar will be on March 30th. <a href="https://flowfuse.com/webinars/2023/introduction-to-flowforge/">Register today</a>.</p>
<h2 id="from-our-blog" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#from-our-blog"></a> From our Blog</h2>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/">Toward Highly Available Node-RED</a> - High availability is an often requested feature for Node-RED. This post from FlowFuse CEO discusses our approach to HA for Node-RED.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/ming-blog/">MING Stack for IoT</a> - MING technology stack include M (Mosquitto/MQTT), InfluxDB, Node-RED and Grafana. Ian Skerrett, FlowFuse Head of Marketing discusses how this tech stack is used for IoT.</p>
<p><a href="https://www.youtube.com/watch?v=47EvfmJji-k">Introduction to Node-RED</a> - An in-depth webinar recording on key Node-RED concepts and demonstration on how to get started with Node-RED.</p>
<p>Node-RED Quick Tips - Rob Marcer, FlowFuse Developer Educator has a weekly series of Node-RED hints and tips</p>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/">Tips #1</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/">Tips #2</a></li>
</ul>
<h2 id="from-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#from-the-community"></a> From the Community</h2>
<h3 id="node-red-community-survey" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#node-red-community-survey"></a> Node-RED Community Survey</h3>
<p>The Node-RED open source project is running an Node-RED Community Survey. Give your <a href="https://nodered.org/blog/2023/02/23/community-survey">feedback on how you are using Node-RED</a>.</p>
<h3 id="good-alternatives-to-pis-for-your-next-project" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#good-alternatives-to-pis-for-your-next-project"></a> Good alternatives to Pis for your next project</h3>
<p>Raspberry Pis continue to be difficult to purchase. Eben Upton of the Raspberry Pi Foundation has said that <a href="https://www.raspberrypi.com/news/supply-chain-update-its-good-news/">supply should improve this year</a> but in the mean-time there are some good alternatives you could consider. The Youtube channel <a href="https://www.youtube.com/@ExplainingComputers">ExplainingComputers</a> has shared a <a href="https://www.youtube.com/watch?v=k8clrUclPIs">great video covering some of the most popular SBCs</a> you could use for your next project, it’s worth a watch.</p>
<h3 id="custom-node-spotlight---node-red-contrib-os" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#custom-node-spotlight---node-red-contrib-os"></a> Custom Node Spotlight - node-red-contrib-os</h3>
<p><a href="https://flows.nodered.org/node/node-red-contrib-os">OS</a> is a great custom node which allows you to monitor the performance of the device you are running Node-RED on. It can check RAM usage, disk space, CPU load, and a lot more. It’s really easy to use, we recommend you <a href="https://flows.nodered.org/node/node-red-contrib-os">take a look</a>.</p>
<h2 id="join-our-team" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/03/community-news-03/#join-our-team"></a> Join Our Team</h2>
<p>FlowFuse is expanding our team. We have two openings right now:</p>
<ul>
<li><strong><a href="https://boards.greenhouse.io/flowfuse/jobs/4798023004">Developer Advocate - Manufacturing & Industrial Automation</a></strong></li>
<li><strong><a href="https://boards.greenhouse.io/flowfuse/jobs/4796271004">DevOps Engineer</a></strong></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/webinar-1-missed-questions/Some questions we didn't get to in our first webinarThere 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.2023-02-27T18:00:00ZRob Marcer<p>There were some great questions from our <a href="https://www.youtube.com/watch?v=47EvfmJji-k">first webinar</a> that we didn't get time to answer, we wanted to share those questions and our answers here.</p>
<!--more-->
<h3 id="irvin-asks%2C-'is-it-possible-to-save-the-debug-information-or-data-flow-to-a-storage-like-splunk-or-sql'%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/webinar-1-missed-questions/#irvin-asks%2C-'is-it-possible-to-save-the-debug-information-or-data-flow-to-a-storage-like-splunk-or-sql'%3F"></a> Irvin asks, 'is it possible to save the debug information or data flow to a storage like Splunk or SQL'?</h3>
<p>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.</p>
<p>I've come across <a href="https://flows.nodered.org/node/node-red-contrib-flogger">Flogger</a> which seems to do a good job of logging, including support for multiple log files, and built in support for log rotation.</p>
<p><picture><source type="image/webp" srcset="https://flowfuse.com/img/flogger-1dLFtSrSrQ-650.webp 650w, https://flowfuse.com/img/flogger-1dLFtSrSrQ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/gif" srcset="https://flowfuse.com/img/flogger-1dLFtSrSrQ-650.gif 650w, https://flowfuse.com/img/flogger-1dLFtSrSrQ-1300.gif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Capturing debug to a log file using Flogger" alt="Capturing debug to a log file using Flogger" loading="lazy" decoding="async" src="https://flowfuse.com/img/flogger-1dLFtSrSrQ-650.webp" width="1300" height="512" /></picture></p>
<p>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.</p>
<h3 id="anonymous-asks-'can-we-make-an-mobile-application-with-node-red-or-can-the-content-only-be-accessed-through-a-web-browser'%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/webinar-1-missed-questions/#anonymous-asks-'can-we-make-an-mobile-application-with-node-red-or-can-the-content-only-be-accessed-through-a-web-browser'%3F"></a> Anonymous asks 'can we make an mobile application with Node-RED or can the content only be accessed through a web browser'?</h3>
<p>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.</p>
<p>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 <a href="https://www.macrumors.com/how-to/add-a-web-link-to-home-screen-iphone-ipad/">iOS</a> and <a href="https://www.androidauthority.com/add-website-android-iphone-home-screen-3181682/">Android</a> support making a home icon. Once added using them is basically the same user experience as a locally installed application.</p>
<h3 id="john-asks-'from-the-random-node-example-in-the-webinar%2C-the-node-connected-to-four-different-nodes.-is-there-an-order-to-which-is-invoked-first%3F-can-that-be-controlled'%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/webinar-1-missed-questions/#john-asks-'from-the-random-node-example-in-the-webinar%2C-the-node-connected-to-four-different-nodes.-is-there-an-order-to-which-is-invoked-first%3F-can-that-be-controlled'%3F"></a> 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'?</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/chart-flow-GUNRkaNFOO-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/chart-flow-GUNRkaNFOO-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing the Flow where the random number generator sends a message to 4 nodes at the same time" alt=""Image showing the Flow where the random number generator sends a message to 4 nodes at the same time"" loading="lazy" decoding="async" src="https://flowfuse.com/img/chart-flow-GUNRkaNFOO-650.jpeg" width="650" height="162" /></picture></p>
<p>All downstream nodes linked to the same prior node will be triggered at practically the same time.</p>
<p>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.</p>
<h3 id="abdelhamid-asks%2C-'how-can-i-delete-a-subflow'%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/webinar-1-missed-questions/#abdelhamid-asks%2C-'how-can-i-delete-a-subflow'%3F"></a> Abdelhamid asks, 'How can I delete a subflow'?</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/delete-subflow-NeWOHmVuKF-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/delete-subflow-NeWOHmVuKF-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Image showing how to delete a subflow" alt=""Image showing how to delete a subflow"" loading="lazy" decoding="async" src="https://flowfuse.com/img/delete-subflow-NeWOHmVuKF-650.jpeg" width="650" height="169" /></picture></p>
<p>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 <a href="https://flowfuse.com/webinars/">webinars page</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/Node-RED Tips - Deploying, Debugging, and DelayingSave yourself time when working on Node-RED with these three tips.2023-02-23T18:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-deploy-just-what-you've-changed" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/#1.-deploy-just-what-you've-changed"></a> 1. Deploy just what you've changed</h3>
<p>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.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/deploy-PFoWtpQ_d0-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Deploying only the changed nodes" alt="Deploying only the changed nodes" loading="lazy" decoding="async" src="https://flowfuse.com/img/deploy-PFoWtpQ_d0-650.webp" width="650" height="233" /></picture></p>
<h3 id="2.-find-which-debug-node-generated-an-entry-in-the-log" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/#2.-find-which-debug-node-generated-an-entry-in-the-log"></a> 2. Find which debug node generated an entry in the log</h3>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/debug-jump-OcDhuaA0Wv-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Finding the debug node which generated the log line" alt="Finding the debug node which generated the log line" loading="lazy" decoding="async" src="https://flowfuse.com/img/debug-jump-OcDhuaA0Wv-650.webp" width="650" height="186" /></picture></p>
<h3 id="3.-the-delay-node-can-be-used-as-a-rate-limiter" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/#3.-the-delay-node-can-be-used-as-a-rate-limiter"></a> 3. The delay node can be used as a rate limiter</h3>
<p>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.</p>
<p>You can use the delay node to limit how many messages can pass through in a given period of time.</p>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/rate-limit-wspGm-Gn8r-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Limiting how many alerts are sent" alt="Limiting how many alerts are sent" loading="lazy" decoding="async" src="https://flowfuse.com/img/rate-limit-wspGm-Gn8r-650.webp" width="650" height="253" /></picture></p>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
<h2 id="enhance-efficiency-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-2/#enhance-efficiency-with-flowfuse"></a> Enhance Efficiency with FlowFuse</h2>
<p>FlowFuse is a cloud-based platform designed to boost collaboration, security, and scalability for your Node-RED applications. It features <a href="https://flowfuse.com/product/features/">numerous tools and functionalities</a> that streamline the development-to-deployment process, including one-click deployment, the <a href="https://flowfuse.com/docs/user/assistant/">FlowFuse Assistant</a>, and other capabilities that simplify managing your Node-RED environment.</p>
<p>For more tips, tricks, and professional development techniques with Node-RED, check out our recommended eBook:</p>
<p><a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">The Ultimate Beginner's Guide to Professional Node-RED</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/ming-blog/MING Stack for IoTA technology stack for IoT2023-02-23T00:00:00Z<p>The folks at Balena have created a bundle they call the <a href="https://hub.balena.io/organizations/marc6/apps/MING">MING stack</a>, (Mosquitto/MQTT, InfluxDB, Node-RED and Grafana) which first appeared back in <a href="https://forums.balena.io/t/ming-an-iot-sensor-stack-mosquitto-influxdb-nodered-grafana/36540">2019</a>. It is an interesting way to look at the IoT tech stack and a pattern I have seen used many times.</p>
<!--more-->
<p>In the early days of the web, the <a href="https://en.wikipedia.org/wiki/LAMP_(software_bundle)">LAMP stack</a> 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/Perl/PHP. 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.</p>
<p>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:</p>
<ul>
<li><a href="https://mosquitto.org/">Mosquitto</a> is the popular open source MQTT broker. MQTT has become the default protocol for IoT communications. The MQTT pub/sub 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.</li>
<li><a href="https://www.influxdata.com/">InfluxDB</a> 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.</li>
<li><a href="https://flowfuse.com/node-red/">Node-RED</a> 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.</li>
<li>Finally <a href="https://grafana.com/">Grafana</a> 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.</li>
</ul>
<p>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.</p>
<p>The LAMP stack was successful because it was:</p>
<ul>
<li>Open source and freely available for anyone to adopt and use.</li>
<li>Highly flexible and customizable that allowed developers to adopt the stack to their use case.</li>
<li>Back by large developer communities creating plugins/extensions, documentation, tutorials, etc.</li>
<li>Easy to learn for developers with limited experience.</li>
</ul>
<p>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.</p>
<p>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?</p>
<h2 id="simplify-iot-complexity-with-flowfuse%E2%80%99s-mind-stack" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/ming-blog/#simplify-iot-complexity-with-flowfuse%E2%80%99s-mind-stack"></a> Simplify IoT Complexity with FlowFuse’s MIND Stack</h2>
<p>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.</p>
<p>With the FlowFuse <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/05/node-red-mind-stack-with-flowfuse/">MIND stack</a>, you get a unified platform that integrates MQTT for efficient communication, Node-RED for seamless integration and data transformation, and <a href="https://flowfuse.com/product/dashboard/">Dashboard 2.0</a> 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.</p>
<p><strong>Want an easier, more secure, and scalable IoT stack? Start your <a href="https://app.flowfuse.com/account/create/">free trial</a> of FlowFuse today.</strong></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/FlowFuse v1.4 with device provisioning in bulk and staged development processOur second release of 2023 with some great new features to try out.2023-02-16T14:00:00Z<p>Deploy Node-RED to many devices quickly, and allow a staged development process with the latest release of FlowFuse v1.4.</p>
<!--more-->
<p>Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above.</p>
<p>To make it easy for everyone to experience FlowFuse, we are introducing a new free 30-day trial. You can now experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your free trial simply <a href="https://app.flowfuse.com/account/create">sign up for FlowFuse Cloud;</a> no credit card is required!</p>
<h2 id="new-user-features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#new-user-features"></a> New User Features</h2>
<p><strong>Automatic Device Provisioning</strong></p>
<p>Most prominently, FlowFuse 1.4 features automatic device onboarding for fleets. Simply download a FlowFuse device provisioning credential to allow quick roll-out to a whole fleet, without the need to have device specific configuration. When the agent starts, the FlowFuse agent and the Node-RED snapshot will automatically be provisioned to the device and start operations. <a href="https://github.com/FlowFuse/flowfuse/issues/1212">Issue #1212</a></p>
<div><iframe width="560" height="315" src="https://www.youtube.com/embed/XTVw4O4-Crg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p><strong>Support for Staged Development</strong></p>
<p>A new feature of 1.4 is the ability to setup staged deployments. This makes it possible to simply move a project between a Development > Test > Production for your Node-RED application delivery. <a href="https://github.com/FlowFuse/flowfuse/issues/1580">Issue #1580</a></p>
<div><iframe width="560" height="315" src="https://www.youtube.com/embed/6QOmotlrwWw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#improvements"></a> Improvements</h2>
<ul>
<li>
<p>It's now much easier to change the resources available to your Node-RED instance. With a few clicks a resource intensive workload can be processed faster by changing between small, medium, or large instance types. <a href="https://github.com/FlowFuse/flowfuse/issues/595">#595</a></p>
</li>
<li>
<p>Last release allows users to capture flows in a shared library to reuse in another flow, now it's possible to preview the stored flows. <a href="https://github.com/FlowFuse/flowfuse/issues/1657">#1657</a></p>
</li>
<li>
<p>Add a synchronous mode for the FlowFuse persisted context store, next to the asynchronous mode already available. <a href="https://github.com/FlowFuse/flowforge-nr-persistent-context/issues/17">#17</a></p>
</li>
<li>
<p>Add a Last Seen status for devices connecting to FlowFuse. <a href="https://github.com/FlowFuse/flowfuse/issues/1599">#1599</a></p>
</li>
<li>
<p>Added a check to ensure the team slug is unique. <a href="https://github.com/FlowFuse/flowfuse/issues/1609">#1609</a></p>
</li>
<li>
<p>Optionally set snapshot as target at creation, to quickly roll out changes to remote deployments <a href="https://github.com/FlowFuse/flowfuse/issues/1527">#1527</a></p>
</li>
<li>
<p>Agents are more rugged when starting up if they're unable to connect to FlowFuse, and will retry to connect.</p>
</li>
<li>
<p>With FlowFuse v1.4 some changes were made under the hood to speed up the recovery
of Node-RED instances. On terminal failures of an instance it will now be
automatically be redeployed with the correct flows. This uses Kubernetes features
and is also available if you've installed through <a href="https://flowfuse.com/docs/install/kubernetes/">kubernetes</a>.
To migrate the old style of deployments to this system a restart or stack upgrade is needed.</p>
</li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li>Deleting your only team, doesn't exit from the team UI. <a href="https://github.com/FlowFuse/flowfuse/issues/1630">#1630</a></li>
<li>Async Team Slug Check. <a href="https://github.com/FlowFuse/flowfuse/issues/1609">#1609</a></li>
<li>Improve communication of Device Last Seen and Status <a href="https://github.com/FlowFuse/flowfuse/issues/1599">#1599</a></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<ul>
<li><a href="https://github.com/UlisesGascon">UlisesGascon</a> for their work on <a href="https://github.com/FlowFuse/installer/pull/74">#74</a></li>
</ul>
<p>As an open-source project, we welcome community involvement in what we're building.
If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.4.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/flowforge-1-4-0-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That's also a great place to send us any feedback or feature requests.</p>
<p>You can also get help on <a href="https://discourse.nodered.org/">the Node-RED forums</a></p>
<p>As well as in the <a href="https://github.com/FlowFuse/flowfuse/discussions">forum within our Github project</a></p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a></p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
</div></div>../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/Toward Highly Available Node-REDHow mission critical use-cases can be supported through FlowFuse soon2023-02-15T00:00:00ZZJ van de Weg<p>Over the past few months we've held a lot of product discovery sessions and a topic
which keeps coming up is "HA Node-RED". All software will have failures, with
HA (high availability) the intent is to allow the workload to be processed
regardless. There's quite a few considerations which are often not covered
during product discovery calls, I'm going to discuss some of those points in this article.</p>
<!--more-->
<p>When my job title was software engineer I was fortunate to design and
implement a HA system. It's an incredibly challenging and rewarding task for
any software engineer. As a topic, it's studied when obtaining a Computer Science
Bachelors degree, masters and even PhD. When tasked
to make a HA system, it took me a good month to define what our goals
were, and what we were willing to exchange for the properties sought. This
might be extra hardware, engineering hours, as well as organizational challenges.
For now, let's focus on the first two.</p>
<p>Let's start with defining the goal; reduce the impact of a Node-RED instance
being unresponsive for an arbitrary reason. In many use-cases the MTTR (Mean Time To
Recovery) is what's measured. When for example a hardware failure takes down the instance and the time to
detection is zero, it will likely still take a few hours to recover. Most of
the recovery work is also manual, and knowledge on how to recover is usually
<a href="https://en.wikipedia.org/wiki/Tribal_knowledge">tribal knowledge</a>. If the right
person is on-site, the right hardware is available, you kept great backups,
and are able to deploy the new hardware right away without support from other
functions you might just achieve an MTTR of 120 minutes!</p>
<h3 id="5-10-minute-mean-time-to-recovery" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/#5-10-minute-mean-time-to-recovery"></a> 5-10 minute Mean Time to Recovery</h3>
<p>What's needed to bring this back to say 10 minutes? First, adopting FlowFuse will
help massively here. FlowFuse can be installed on-premise, or you can use our
managed Cloud offering. The software is the same, provided the on-premise
install uses our <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes install</a> method.</p>
<p>The key of the installation is the fact that the hardware layer is generalized
as a fleet. Detecting failures is included in the install, and very fast. Comparing
that to most alerting systems currently, it's usually a difference between night
and day. Furthermore, to decrease the recovery time significantly
there's a requirement to make software responsible for the whole procedure. Human intervention is much too slow.</p>
<p>To get the MTTR down to 5 minutes there's a requirement to either make hardware
automatically available to the fleet, or to over-provision (more hardware is
available than is needed at any given moment). When a hardware failure occurs
FlowFuse is configured to ensure all Node-RED instances that are KIA are
replaced. Bringing down the time to recovery to about 5 minutes.
For many use-cases a MTTR of 5 minutes is <em>good enough</em>.</p>
<h3 id="sub-minute-mttr" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/#sub-minute-mttr"></a> Sub minute MTTR</h3>
<p>To go below the minute, or dare I say go below 10 seconds, we'll need to increase
the number of running Node-RED instances. Let's start with a hot-spare. Meaning
there's a running Node-RED instance with the flows exactly the same as another,
ready to pick up the work when the first has some failure. Note this isn't like
a relay race, there's no baton being passed from one Node-RED to the other. While
some data and messages might be lost, it's possible to redirect all workload from
the plagued Node-RED to the hot-spare in a matter of seconds. Hot-spares taking
over are usually only observed by humans a good few minutes after they replace a failed instance.</p>
<h3 id="sub-second!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/#sub-second!"></a> Sub second!</h3>
<p>Before this post turns into a theoretical exercise we'd really need to understand
which trade-offs are acceptable to you. There's the <a href="https://en.wikipedia.org/wiki/CAP_theorem">CAP Theorem</a>
which states 3 guarantees are wanted: Consistency, Availability, and Partition Tolerance. You get to pick
only two. In manufacturing the line must never be stopped due to a software failure where possible,
so Availability is the most important. The question is, what comes next? Is it Consistency meaning
all 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?</p>
<p>Whichever 2 you choose will dictate engineering choices in the pursuit of a great HA solution.</p>
<h3 id="the-roadmap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/highly-available-node-red/#the-roadmap"></a> The roadmap</h3>
<p>With FlowFuse v1.4, released February 2023, a 5 minute mean time to recovery is
achieved for all flows running locally, that is: in the cluster. Going beyond this
milestone requires your input! I'd love to chat about your challenges, please
<a href="https://meetings-eu1.hubspot.com/zeger-jan">pick a timeslot to discuss your requirements</a>!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/Service Disruption Report for January 27th, 20232023-02-10T00:00:00ZNick O'Leary<p>On January 27th, 2023, we were alerted to an issue on FlowFuse Cloud where a user
was not able to access a newly created Node-RED Project, receiving a 404 error
instead. This post examines the issue that was hit, the timeline of events and
what we've done to resolve it.</p>
<!--more-->
<h2 id="summary" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/#summary"></a> Summary</h2>
<p>We hit a limit in the AWS Load Balancer that capped how many projects could be
exposed to the internet within our FlowFuse Cloud deployment. The result of this
was that users could create a new Node-RED project, but they would not be able
to access the editor.</p>
<p>We freed up capacity on the platform to allow user projects to be created without
hitting the limit, whilst also asking AWS to increase the limit in question which
they duly did.</p>
<p>However, we later discovered a second limit that was also being applied. That limit
was not one AWS permits us to change.</p>
<p>We successfully completely deployment of a change to our platform architecture
today that removes these limits from our environment.</p>
<p>In total, this lead to approximately 2 hours of disruptions on January 27th 2023 and again
on February 8th 2023 during which newly created Node-RED projects were not accessible.</p>
<p>Our logs show that two users were impacted during these times.</p>
<h2 id="technical-details" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/#technical-details"></a> Technical Details</h2>
<p>When running FlowFuse within a Kubernetes environment, each project creates a
new Ingress Object configuration to tell the platform how to route HTTP traffic
to that project.</p>
<p>Our FlowFuse Cloud deployment runs within Amazon Elastic Kubernetes Service (EKS)
and uses the Application Load Balancer (ALB) service as its ingress controller.</p>
<p>When the FlowFuse platform creates the new Ingress Object configuration, EKS passes
that to ALB to generate the necessary configuration, which, given the configuration we
were using, created both a Target Group and Rule object.</p>
<p>With a default of limit of 100 Target Groups and Rules, that meant we had a technical
limit of 100 Node-RED projects within the FlowFuse Cloud environment. Increasing
the Rule limit did not solve the problem as the Target Group limit still applied.</p>
<p>Our initial mitigation was to delete any Node-RED projects we had created for
our own internal testing. We also identified that we could safely delete any rules
for suspended projects. A suspended project is one that is not actively running
in the platform. The code that resumes a suspended project would recreate any ingress
objects needed - so deleting the rule whilst suspended would not have any impact
on the project.</p>
<p>This gave us a small amount of headroom on the platform which crucially meant we
had time to develop a longer term solution.</p>
<h2 id="resolution" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/#resolution"></a> Resolution</h2>
<p>There were two possible routes we could take to resolve this issue:</p>
<ul>
<li>
<p>Investigate how to reuse existing Target Groups rather than create one for each project</p>
<p>Our intial research was inconclusive on how to achieve this. The AWS docs weren't
clear enough to give us a definitive answer we felt comfortable to invest our time
in.</p>
</li>
<li>
<p>Move away from ALB in favour of nginx to provide our ingress load balancing.</p>
<p>This was always our long term strategy as it was a prerequisite to FlowFuse
features we have in the roadmap such as providing custom domains to projects.
However it was potentially a large piece of work with a complicated migration
for the existing environment.</p>
</li>
</ul>
<p>Given it fitted with our longer-term strategic goals, we decided to move ahead
with replacing ALB with nginx.</p>
<p>After some initial development work and experimentation, we felt comfortable that
the migration was not as complicated as initially feared. We would have to manually
copy the existing ALB rules over - something that could be scripted. Once we had
nginx deployed we could push a small code change to FlowFuse to use it rather than
ALB, and also switch over the DNS entry to point at nginx.</p>
<p>Following a successful run through in our testing/staging environment, we decided
to move ahead updating the production environment.</p>
<p>This change was applied today, whilst we closely monitored the system to ensure
no further disruption occurred. We have validated that new projects can be
created without issue and everything is working as it should.</p>
<h2 id="next-steps" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/#next-steps"></a> Next Steps</h2>
<p>With FlowFuse Cloud updated to use the new load balancer, we'll be closely monitoring
it over the next few days to ensure it operates normally.</p>
<p>We will also be taking on some follow-up activities to minimise the risk of this
type of issue happening again:</p>
<ol>
<li>Review all AWS limits within our architecture. Identify any that pose a potential
issue in the future. Ensure they are documented and a plan put in place to mitigate
the impact based on our expected platform growth.</li>
<li>Add additional external monitoring for project liveness.</li>
<li>Review all logging around k8s apis</li>
</ol>
<h2 id="timeline" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/service-disruption-report-2023-01-27/#timeline"></a> Timeline</h2>
<p><em>All times are GMT.</em></p>
<p><strong>2023-01-27 22:15</strong> : (Friday evening) Customer reports via our support channel a newly created project was not accessible and returning a 404 error.</p>
<p><strong>2023-01-27 22:22</strong> : We start examining the platform logs</p>
<p><strong>2023-01-27 22:47</strong> : We identify we've hit the default Rules limit on the AWS Application Load Balancer</p>
<p><strong>2023-01-27 23:15</strong> : To free-up capacity on the platform we delete any ununsed internal projects we were using for general testing. We also identify we can safely delete any rules associated with suspended projects.</p>
<p><strong>2023-01-27 23:41</strong> : We complete deleting rules to give us enough head-room to see us through the weekend.</p>
<p><strong>2023-01-30 10:00</strong> : We submit a request to AWS to increase the rule limit to 200 which is accepted and actioned later that day.</p>
<p>...</p>
<p><strong>2023-02-08 14:05</strong> : Another customer reports seeing a newly created project returning a 404 error. We examine the ALB configuration and whilst it reports the new limit has been changed to 200, it appears to still be limiting at 100. We start identifing more suspended projects we can delete the rules for to free up capacity.</p>
<p><strong>2023-02-08 14:20</strong> : Sufficient capacity is freed to enable the customer's projects to be accessible.</p>
<p><strong>2023-02-08 15:00</strong> : We identify we've hit the ALB Target Group limit. This is a hard limit that AWS does not allow you to change. We begin researching options.</p>
<p><strong>2023-02-09</strong> : Commited to plan to replace ALB with nginx. Successful migration of our staging environment.</p>
<p><strong>2023-02-10</strong> : Change applied to production, FlowFuse 1.3.3 deployed and DNS updated to use the new load balancer.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/Node-RED Tips - Wiring ShortcutsSave yourself time when working on Node-RED with these three tips.2023-02-07T18:00:00ZRob Marcer<p>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.</p>
<!--more-->
<h3 id="1.-use-control%2Bleft-click-to-search-your-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/#1.-use-control%2Bleft-click-to-search-your-nodes"></a> 1. Use control+left-click to search your nodes</h3>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/load-node-icrUMTTLfp-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Selecting a node without having to use the palette" alt="Selecting a node without having to use the palette" loading="lazy" decoding="async" src="https://flowfuse.com/img/load-node-icrUMTTLfp-650.webp" width="650" height="267" /></picture></p>
<h3 id="2.-split-sections-of-your-code-using-the-link-nodes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/#2.-split-sections-of-your-code-using-the-link-nodes"></a> 2. Split sections of your code using the link nodes</h3>
<p>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 <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/">Node-RED best practices</a>, the combination of link nodes and grouped flows is very powerful.</p>
<p>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'.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/split-with-link-0ciXwYQY_N-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Spliting your nodes with link nodes" alt="Spliting your nodes with link nodes" loading="lazy" decoding="async" src="https://flowfuse.com/img/split-with-link-0ciXwYQY_N-650.webp" width="650" height="393" /></picture></p>
<h3 id="3.-link-multiple-inputs-and-outputs-in-one-command" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/3-quick-node-red-tips-1/#3.-link-multiple-inputs-and-outputs-in-one-command"></a> 3. Link multiple inputs and outputs in one command</h3>
<p>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.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/join-wires-in1O07LX1I-538.gif 538w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Linking multiple inputs and outputs in one command" alt=""Linking multiple inputs and outputs in one command"" loading="lazy" decoding="async" src="https://flowfuse.com/img/join-wires-in1O07LX1I-538.webp" width="538" height="362" /></picture></p>
<p>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 <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/Community News February 2023Your monthly update for the FlowFuse and Node-RED communities2023-02-02T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter for February 2023, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you think we should share on our newsletters please <a href="mailto:contact@flowfuse.com/">get in touch</a>.</p>
<h2 id="news" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#news"></a> News</h2>
<h3 id="node-red-ask-me-anything" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#node-red-ask-me-anything"></a> <a href="https://flowfuse.com/ask-me-anything/ama-nodered/">Node-RED Ask Me Anything</a></h3>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ama-feb-B_sib6yUmF-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ama-feb-B_sib6yUmF-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="AMA Session with Nick O'Leary and Rob Marcer" alt="AMA Session with Nick O'Leary and Rob Marcer" loading="lazy" decoding="async" src="https://flowfuse.com/img/ama-feb-B_sib6yUmF-650.jpeg" width="650" height="341" /></picture></p>
<p>Do you have any questions about Node-RED or need some advice on a tricky issue using Node-RED? Here is your opportunity to get help from the experts.</p>
<p>Nick O'Leary, co-founder of Node-RED & CTO of FlowFuse, and Rob Marcer, Node-RED community member & Developer Educator at FlowFuse, will be hosting an interactive Ask Me Anything session. This is a great opportunity to ask questions of the Node-RED experts. If you have any questions for Nick and Rob you can send them in before the session using <a href="https://docs.google.com/forms/d/e/1FAIpQLSdfPq4lAQjdvqhTpoYtKiMNgP8vcMhZsAf_AG0MHuVMRK83_Q/viewform">this form</a>.</p>
<h3 id="free-flowfuse-project-for-30-days%2C-no-catches" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#free-flowfuse-project-for-30-days%2C-no-catches"></a> <a href="https://app.flowfuse.com/account/create">Free FlowFuse project for 30 days, no catches</a></h3>
<p>To make it easy for everyone to experience FlowFuse, we are introducing a new <a href="https://app.flowfuse.com/account/create">free 30-day trial</a>. With this trial, you can experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your trial simply <a href="https://app.flowfuse.com/account/create">sign up for a new FlowFuse team</a>.</p>
<h3 id="1.3-released" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#1.3-released"></a> <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/">1.3 Released</a></h3>
<p>Version 1.3 of FlowFuse was released on 19th January. Our first release of 2023 included some great new features such as the ability to share flows via a <a href="https://www.youtube.com/watch?v=B7XK3TUklUU">team library</a>, <a href="https://www.youtube.com/watch?v=JRk-Cf7eNIo">control access to your Node-RED dashboards</a> using FlowFuse credentials, and <a href="https://www.youtube.com/watch?v=p0Vuy5x42Go">filtering on your audit logs</a> for easier reading. We also added the ability to use FlowFuse on devices which cannot access npm, we think this will be really valuable to users of networks with limited access to the internet.</p>
<p>We're now working towards release 1.4 which is due on 16th February. You can see what we are planning to deliver in that release and beyond on <a href="https://github.com/orgs/FlowFuse/projects/5">FlowFuse's project board</a>.</p>
<p>If you’d like to learn more about what else was included in 1.3 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/">blog post</a>, <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v1.3.0">GitHub release page</a>, and <a href="https://www.youtube.com/watch?v=ey3xv5j5x7k">Youtube channel</a>.</p>
<h3 id="team-news" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#team-news"></a> <a href="https://flowfuse.com/team/">Team News</a></h3>
<p>We are currently recruiting <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004">NodeJS Developers</a> as well as a <a href="https://boards.greenhouse.io/flowfuse/jobs/4785058004">Graphic Designer</a> to join our team. You can view any of the roles we currently have open and apply on our <a href="https://boards.greenhouse.io/flowfuse">Jobs page</a>.</p>
<h2 id="node-red-in-the-community" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#node-red-in-the-community"></a> Node-RED in the Community</h2>
<h3 id="twitch-streamer-beats-elden-ring-boss-using-mind-control-(and-a-little-node-red)" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#twitch-streamer-beats-elden-ring-boss-using-mind-control-(and-a-little-node-red)"></a> <a href="https://www.vice.com/en/article/bvmqmm/watch-an-elden-ring-streamer-beat-a-boss-using-her-thoughts">Twitch Streamer beats Elden Ring boss using mind control (and a little Node-RED)</a></h3>
<p><a href="https://www.vice.com/en/article/bvmqmm/watch-an-elden-ring-streamer-beat-a-boss-using-her-thoughts"><picture><source type="image/avif" srcset="https://flowfuse.com/img/twitch-slgwXSc2_C-500.avif 500w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/twitch-slgwXSc2_C-500.webp 500w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Twitch Streamer beats Elden Ring boss using mind control (and a little Node-RED)" loading="lazy" decoding="async" src="https://flowfuse.com/img/twitch-slgwXSc2_C-500.jpeg" width="500" height="281" /></picture></a>
Streamer <a href="https://www.twitch.tv/videos/1717013810">Perrikaryal</a> used an electroencephalography (EEG) headset to read her brain activity which in turn sends commands via Node-RED to her gaming computer. She then proceeded to use that control method to beat one of the harder bosses in Elden Ring, a notoriously difficult game to start with. The end result is a great example of how Node-RED can link disparate tech together easily. You can <a href="https://www.twitch.tv/videos/1722048787">watch the full stream on Twitch</a>.</p>
<h3 id="run-node-red-in-a-web-browser%2C-client-side!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#run-node-red-in-a-web-browser%2C-client-side!"></a> <a href="https://www.linkedin.com/posts/kazuhitoyokoi_nodered-webassembly-activity-7015696090112958464-F3MA/?utm_source=share&utm_medium=member_android">Run Node-RED in a web browser, client side!</a></h3>
<p><a href="https://www.linkedin.com/in/kazuhitoyokoi/">Kazuhito Yokoi</a> has implemented an early prototype of Node-RED runtime for WebAssembly. As a demonstration, he built a simple flow which shows the current location of the International Space Station (ISS). This proof-of-concept shows that an entire Node-RED flow can be executed in a browser. We think this could be a very useful option for running Node-RED in places where it wasn’t previously practical. You can view his work on this <a href="https://github.com/kazuhitoyokoi/node-red-wasm">GitHub project</a>.</p>
<h3 id="custom-node-spotlight---moment" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/02/community-news-02/#custom-node-spotlight---moment"></a> <a href="https://flows.nodered.org/node/node-red-contrib-moment">Custom Node Spotlight - Moment</a></h3>
<p><a href="https://flows.nodered.org/node/node-red-contrib-moment"><picture><source type="image/avif" srcset="https://flowfuse.com/img/moment-74qvydlV7--650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/moment-74qvydlV7--650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Moment converting a timestamp to ISO standard date and time" loading="lazy" decoding="async" src="https://flowfuse.com/img/moment-74qvydlV7--650.jpeg" width="650" height="106" /></picture></a>
Being able to easily switch dates and times from one format to another is a huge timesaver, node-red-contrib-moment makes those tasks a breeze. The package actually includes two custom nodes, the first ‘Moment’ produces a nicely formatted Date/Time string using the Moment.JS library. The second custom node ‘Humanizer’ converts time durations (time spans) into textual descriptions (e.g. 2 minutes). We recommend you keep it in mind for any flows working with time conversions.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-story/Telling the FlowFuse StoryFlowFuse helps organizations reliably deliver Node-RED applications2023-02-02T00:00:00Z<p>During my first month at FlowFuse, I have been helping to refine the FlowFuse story. Trying to answer the questions: What is the value FlowFuse brings to our customers and what features does FlowFuse want to offer the Node-RED community?</p>
<!--more-->
<p>People love Node-RED because it makes it trivial to connect different types of services and data, create flows of the data, and store/forward data to the cloud or a database. The Node-RED <a href="https://flows.nodered.org/">library of nodes and flows</a> enables connections to almost anything. The speed and ease of use of Node-RED allows non-traditional programmers to accelerate their innovation and exploration of what is possible.</p>
<p>Node-RED is great for individuals to unlock what is possible for an organization. However, there are some challenges if you want to scale Node-RED into a corporate development platform. Challenges like how do you set up a development -> test -> production delivery pipeline, how to share instances with different developers, how do you create repeatable release processes, how can you deploy the same Node-RED instance out to multiple target environments (devices or servers).</p>
<p>Today, we see many industrial engineers using Node-RED to collect and integrate data from different factory and industrial equipment. Many PLC vendors, like Opto22, are enabling this by making Node-RED as a development environment for the PLC platform. In the manufacturing and industrial automation industries, benefits like reliability and security are important for the continuous operation of these facilities. This is why we believe these types of organizations will be looking for tools that improve their software development lifecycle.</p>
<p>In the larger software development community, DevOps tools are being used to address many of these same challenges. DevOps is all about increasing the reliability and speed of the software development lifecycle. Using tools that increase automation of the software development process and improve collaboration and communication between developers. These are also many of the same challenges that FlowFuse solves for Node-RED users.</p>
<p>For these reasons, we see FlowFuse as being the DevOps platform for Node-RED. We believe FlowFuse enables a more reliable way to deliver Node-RED applications in a repeatable, collaborative and secure manner. What FlowFuse adds to Node-RED are some key features:</p>
<ul>
<li>Tools for collaborative Node-RED development</li>
<li>The ability to manage remote deployments of Node-RED instances</li>
<li>A more streamline way to deliver Node-RED applications</li>
<li>FlowFuse Cloud provides a hosted Node-RED platform but FlowFuse can also be self-hosted</li>
<li>FlowFuse also offers professional technical support to organizations that require the assurance of receiving help with production deployments.</li>
</ul>
<p>We will be rolling out this new way of talking about FlowFuse over the next couple of days and weeks. We are always open to feedback about FlowFuse and would love to hear about your experience of using Node-RED.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/Using Environment Variables in Node-REDPredefined data to be used in your Node-RED instance2023-01-27T16:11:47ZZJ van de Weg<p>Programs, written with Node-RED or otherwise, need to sometimes retrieve information that wasn’t decided on during the creation of the program.</p>
<!--more-->
<p>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 <code>API_KEY</code> with the value set to <code>yoursupersecretkey</code>. FlowFuse allows setting environment variables. Let’s start using them to understand how they work.</p>
<p>One of the options for the <code>inject</code> node is to inject a <code>env variable</code>, short for; you guessed it: Environment Variable. In this case we’re going to one that’s pre-defined by Node-RED: <code>NR_FLOW_NAME</code>. The name of each variable is in all caps by convention. When connecting this inject to a debug it prints “Flow 1” for me.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/node-red-use-env-var-wdx7VXtR52-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/node-red-use-env-var-wdx7VXtR52-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Using an environment variable in Node-RED" alt="Using an environment variable in Node-RED" loading="lazy" decoding="async" src="https://flowfuse.com/img/node-red-use-env-var-wdx7VXtR52-650.jpeg" width="650" height="551" /></picture></p>
<p>Leveraging environment variables can also be done with other nodes, like for example <code>change</code>, <code>switch</code>. Note however; you can set the <code>inject</code> node to output the value for <code>FOO</code> even when it doesn’t exist, but it doesn’t allow you to check in the switch node for example if <code>FOO</code> exists.</p>
<p>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 <a href="https://flowfuse.com/docs/user/persistent-context/">persistent context</a>. 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.</p>
<h3 id="predefined-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/#predefined-variables"></a> Predefined variables</h3>
<p>Our first example was using a predefined variable, exposed by Node-RED. As of 3.0 it exposes a few environment variables among which <code>NR_NODE_NAME</code>, <code>NR_GROUP_NAME</code>, and <code>NR_FLOW_NAME</code>.</p>
<p><a href="https://flowfuse.com/">FlowFuse</a> extends this list with for example a <code>FF_PROJECT_ID</code> allowing you to for example understand what group of instances sent a certain message, but also sets them for each <a href="https://flowfuse.com/docs/device-agent/introduction/">device agent</a>. This allows users to pinpoint which device sent a message, for example to update a dashboard accordingly.</p>
<h3 id="managing-environments-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/#managing-environments-variables"></a> Managing environments variables</h3>
<p>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.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-650.avif 650w, https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-650.webp 650w, https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-650.jpeg 650w, https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Setting a environment variable in FlowFuse" alt=""Setting a environment variable in FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowforge-set-env-var-DORJCSNpE8-650.jpeg" width="1300" height="393" /></picture></p>
<h2 id="boost-your-node-red-security-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/#boost-your-node-red-security-with-flowfuse"></a> Boost Your Node-RED Security with FlowFuse</h2>
<p>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.</p>
<p>Learn how FlowFuse can boost your Node-RED security and streamline management through the <a href="https://flowfuse.com/product/security/#application">FlowFuse security statement</a>.</p>
<h3 id="explore-more-on-security" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/environment-variables-in-node-red/#explore-more-on-security"></a> Explore More on Security</h3>
<ul>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/04/role-based-access-control-rbac-for-node-red-with-flowfuse/">Role-Based Access Control (RBAC) for Node-RED with FlowFuse</a></li>
<li><a href="https://flowfuse.com/docs/user/devops-pipelines/#protected-instances">Protecting Instances from Being Modified</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-ldap-for-the-node-red/">How to Set Up SSO LDAP for Node-RED</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/07/how-to-setup-sso-saml-for-the-node-red/">How to Set Up SSO SAML for Node-RED</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/03/http-authentication-node-red-with-flowfuse/">Securing HTTP Traffic for Node-RED with FlowFuse</a></li>
<li><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2024/01/soc2/">FlowFuse is now SOC 2 Type 1 Compliant</a></li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/Getting Started with Node-REDNode-RED is one of the easiest ways to program ever created but everyone needs a little help2023-01-23T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>In this blog post, we'll take a look at how to get started with Node-RED and create some basic flows. Additionally, we will explore the use of the palette manager, a powerful feature that allows users to install and manage additional nodes for Node-RED.</p>
<h3 id="installing-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#installing-node-red"></a> Installing Node-RED</h3>
<p>First, you'll need to get an installation of Node-RED up and running. There are several ways to do this, we would suggest you use 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.</p>
<h4 id="flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#flowfuse"></a> FlowFuse</h4>
<p>To get Node-RED running on FlowFuse <a href="https://app.flowfuse.com/account/create">sign up as a new user</a>.
New users are enrolled in a trial and a Node-RED will be started for you within a minute.</p>
<p>Once that instance has booted up you access Node-RED by pressing "Open Editor".</p>
<h4 id="npm" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#npm"></a> npm</h4>
<p>To install Node-RED locally using npm, open up your terminal and type the following command:</p>
<div style="position: relative" id="code-container-28">
<pre class="language-bash"><code id="code-28" class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> node-red</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-28" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Once Node-RED is installed, you can start it by running the following command:</p>
<div style="position: relative" id="code-container-32">
<pre class="language-bash"><code id="code-32" class="language-bash">node-red</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-32" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>This will start the Node-RED server and open up the <a href="http://localhost:1880/">editor in your web browser</a>. You can also specify a different port or a settings file if you want to.</p>
<h3 id="first-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#first-flow"></a> First Flow</h3>
<p>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 <code>http in</code>, <code>http response</code>, and the <code>change</code> nodes, which can be found in the common nodes menu on the left of Node-RED.</p>
<p>First, drag an <code>http in</code> node into the editor. This node will listen for incoming HTTP requests. Next drag in the "change" and the <code>http response</code> node into the editor. Connect the <code>http in</code> node to the <code>change</code> node and connect the <code>change</code> node to the <code>http response</code> node. Hopefully your flow looks similar to this:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/three-nodes-BCZadG3HQ2-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/three-nodes-BCZadG3HQ2-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration." alt=""Screenshot showing the HTTP-in, Change, and HTTP-response nodes that we will be using throughout this blog for demonstration."" loading="lazy" decoding="async" src="https://flowfuse.com/img/three-nodes-BCZadG3HQ2-650.jpeg" width="650" height="169" /></picture></p>
<p>To configure the <code>http in</code> 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 <code>/hello</code> and the method to <code>GET</code>.</p>
<p>Now we need to set what the endpoint will respond with, we will do that in the <code>change</code> node.
Double-click the <code>change</code> node then add "Hello World" to the field which says "to the value". It should look like this:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/set-reply-0Y7r7JtsCl-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/set-reply-0Y7r7JtsCl-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Configuring the change node to set the payload to Hello World" alt=""Configuring the change node to set the payload to Hello World"" loading="lazy" decoding="async" src="https://flowfuse.com/img/set-reply-0Y7r7JtsCl-650.jpeg" width="650" height="408" /></picture></p>
<p>To configure the <code>http response</code> 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 <code>http response</code> node should look:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/response-code-cMASPVLuiT-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/response-code-cMASPVLuiT-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Configuring the status node to set the response to 200" alt=""Configuring the status node to set the response to 200"" loading="lazy" decoding="async" src="https://flowfuse.com/img/response-code-cMASPVLuiT-650.jpeg" width="650" height="343" /></picture></p>
<p>You can read more about HTTP response codes in <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes">this article</a>.</p>
<h3 id="testing-your-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#testing-your-flow"></a> Testing Your Flow</h3>
<p>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 "http://localhost:1880/hello". If you are working on FlowFuse take the URL of your project and add "/hello" to the end, it should looks something like this "https://your-instance-name.flowfuse.cloud/hello". You should see "Hello World!" displayed in the browser.</p>
<h3 id="debug-output" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#debug-output"></a> Debug Output</h3>
<p>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 in the right side of the editor. This can be very helpful when trying to understand how a flow is working or troubleshoot any issues.</p>
<h3 id="the-palette-manager" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#the-palette-manager"></a> The Palette Manager</h3>
<p>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.</p>
<h3 id="import-the-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#import-the-flow"></a> Import the flow</h3>
<p>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.</p>
<div id="nr-flow-107" style="height: 200px" data-grid-lines="true" data-zoom="true" data-images="true" data-link-lines="false" data-labels="true"></div>
<script type="module">const flow107 = "\n[\n {\n \"id\": \"a742e7a95697bb40\",\n \"type\": \"http in\",\n \"z\": \"9e9af3caa4dc14d3\",\n \"name\": \"\",\n \"url\": \"/hello\",\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\": []\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]\n";
new FlowRenderer().renderFlows(JSON.parse(flow107.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&')), { container: document.getElementById('nr-flow-107') })</script>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/getting-started-with-node-red/#what's-next%3F"></a> What's Next?</h3>
<p>Well done, hopefully you've now got your first flow up and running and your mind is racing with all the possibilities of what comes next. Enjoy using Node-RED and thanks for reading. If you'd like to dive deeper into more Node-RED capabilities and how it can help in an enterprise setting, check out our <a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">eBook The Ultimate Beginner Guide to a Professional Node-RED</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/FlowFuse 1.3 is now available, share your flows through our new team libraries and much moreOur first release of 2023 with some great new features to try out, happy new year from everyone at FlowFuse!2023-01-19T18:00:00ZRob Marcer<p>Share your flows via team libraries, control access to your Node-RED dashboards using FlowFuse credentials, and filter your audit logs by users and actions.</p>
<!--more-->
<p>We're pleased to announce version 1.3 is now available! Due to the recent holiday season, most of our team have been away from their desks but we still have some great new features to share. Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above.</p>
<p>To make it easy for everyone to experience FlowFuse, we are introducing a new <a href="https://app.flowfuse.com/account/create">free 30-day trial</a>. With this trial, you can experience the power of using FlowFuse to quickly deliver Node-RED applications in a reliable, repeatable, collaborative, and secure manner. To get your trial simply <a href="https://app.flowfuse.com/account/create">sign up for a new FlowFuse team</a>.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/237">Share your flows via team libraries</a> <br />
FlowFuse has now added the ability for you to share your flows via the import and export features in Node-RED. Once you export a flow everyone else in your FlowFuse team will be able to import your work into their projects. You can see a demonstration of this new feature in <a href="https://youtu.be/B7XK3TUklUU">the video</a> below.</p>
<div><iframe width="560" height="315" src="https://www.youtube.com/embed/B7XK3TUklUU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/1325">Control access to your Node-RED dashboards using FlowFuse credentials</a> <br />
In FlowFuse 0.10 we added the ability to secure endpoints created within your FlowFuse projects. This allows you to create dashboards or APIs and limit who can access them. In 1.3 we've added the ability for you to limit access to those same resources based on the visitor having a user account on your FlowFuse team. You can see a demonstration of this new feature in <a href="https://youtu.be/JRk-Cf7eNIo">the video</a> below.</p>
<div><iframe width="560" height="315" src="https://www.youtube.com/embed/JRk-Cf7eNIo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/1448">Filter your audit logs for easier reading</a> <br />
In FlowFuse 1.3 we’ve added the ability to filter your admin logs by user or action type. We think this is a great new feature which will help admins have confidence that they will be able to review the audit logs quickly when needed. You can see a demonstration of this new feature in <a href="https://youtu.be/p0Vuy5x42Go">the video</a> below.</p>
<div><iframe width="560" height="315" src="https://www.youtube.com/embed/p0Vuy5x42Go" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#improvements"></a> Improvements</h2>
<p><a href="https://github.com/FlowFuse/device-agent/issues/45">Allow installation of FlowFuse on devices which can't access npm</a> <br />
We've had feedback from customers that in some cases they want to use FlowFuse devices on hardware which cannot access <a href="https://www.npmjs.com/">Node Package Manager</a> (npm). In a standard configuration of Node-RED, access to npm is mandatory to run your flows. In FlowFuse 1.3.0 we've added the ability for you to import all the data usually installed from npm without your devices having access to the npm service.</p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li>Project status UI sometimes getting stuck when restarting <a href="https://github.com/FlowFuse/flowfuse/issues/1232">#1232</a></li>
<li>SSO users asked to click link in email to verify <a href="https://github.com/FlowFuse/flowfuse/issues/1543">#1543</a></li>
<li>SSO users unable to edit settings <a href="https://github.com/FlowFuse/flowfuse/issues/1542">#1542</a></li>
<li>SSO users not redirected to editor when signing in <a href="https://github.com/FlowFuse/flowfuse/issues/1481">#1481</a></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#contributors"></a> Contributors</h2>
<p>We'd like to thank the following for their contributions to this release:</p>
<p><a href="https://github.com/flecoufle">flecoufle</a> for their work on <a href="https://github.com/FlowFuse/helm/pull/89">#89</a></p>
<p>As an open-source project, we welcome community involvement in what we're building.
If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h2 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#try-it-out"></a> Try it out</h2>
<p>We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install FlowFuse yourself via a variety of install options. You can find out more details <a href="https://flowfuse.com/docs/install/introduction/">here</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create">Get started for free</a> on FlowFuse Cloud.</p>
<h2 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h2>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.3.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h2 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1-3-0-released/#getting-help"></a> Getting help</h2>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That's also a great place to send us any feedback or feature requests.</p>
<p>You can also get help on <a href="https://discourse.nodered.org/">the Node-RED forums</a></p>
<p>AS well as in the <a href="https://github.com/FlowFuse/flowfuse/discussions">forum within our Github project</a></p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a></p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
</div></div></div>../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1.2.1-released/FlowFuse 1.2.1 releasedRelease includes fix for emailing new user2023-01-12T12:00:00ZBen Hardill<p>We've published a maintenance release containing a fix for new users.</p>
<!--more-->
<p>This release fixes an <a href="https://github.com/FlowFuse/flowfuse/issues/1537">issue</a> introduced in FlowFuse 1.2 where users were not being sent their welcome email when they first sign up to the platform. As the sign-up page asks them to click on the link in that email, it left them waiting for an email that wasn't going to arrive.</p>
<p>The same fix also addresses an <a href="https://github.com/FlowFuse/flowfuse/issues/1514">issue</a> where users could not sign up using an email with a <code>+</code> in it. This is a restriction we apply to users signing up with Single Sign-On enabled email domains - but the check was being applied to everyone.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1.2.1-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>This release has already been rolled out to <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a></p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/flowforge-1.2.1-released/#getting-help"></a> Getting help</h3>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform, please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<p>You can also raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2023/01/community-news-12/Community News December 2022News from the FlowFuse and Node-RED communities2023-01-12T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter for December 2022, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community. 2022 was a really exciting year for the development of both Node-RED and FlowFuse. Node-RED released version 3, another major milestone for the project. FlowFuse hit version 1, leaving the beta development stage. We are excited to see what the community can achieve in 2023!</p>
<!--more-->
<p>If you've got something that you think we should share on our newsletters please <a href="mailto:contact@flowfuse.com/">get it touch</a>.</p>
<p><a href="https://github.com/node-red/node-red/milestone/19"><strong>Node-RED Nears 3.1 Release</strong></a>
The release of Node-RED 3.1 is expected very soon. 3.1 includes lots of great new features such as support for <a href="https://github.com/node-red/node-red/pull/3938">locking flows in the editor</a> and <a href="https://github.com/node-red/node-red/pull/3930">improving the user experience around hiding flows</a>. As an open source project the development of Node-RED is entirely dependent on individuals and companies giving their time to work towards each new release. If you'd like to know how you can get involved you can read more on the <a href="https://nodered.org/about/contribute/">Node-RED web site</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/"><strong>FlowFuse 1.2 Released</strong></a><br />
Version 1.2 of FlowFuse was released on 23rd December. Our last release of the year included some great new features such as <a href="https://flowfuse.com/docs/cloud/introduction/#single-sign-on">Single Sign-On</a> to make it easier for your team to access your projects and <a href="https://flowfuse.com/docs/cloud/introduction/#node-red-context">Persistent Context</a> which allows you to retain context values even when restarting projects. We're now working towards our first release of 2023 which is due on 19th January. You can see what we are planning to deliver in that release and beyond on <a href="https://github.com/orgs/FlowFuse/projects/5">FlowFuse's project board</a>.</p>
<p>If you’d like to learn more about what else was included in 1.2 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/">blog post</a>, <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v1.2.0">GitHub release page</a>, and <a href="https://www.youtube.com/watch?v=u7TjqUAub1g">Youtube channel</a>.</p>
<p><a href="https://iot-analytics.com/5-iot-sensor-technologies/"><strong>5 IoT Sensor Technologies to Watch</strong></a><br />
According to this detailed article from iot-analytics.com, on average, four new sensors are connected with every new IoT device that comes online. With approximately 14 billion current IoT connections, this means more than 50 billion connected sensors have been deployed. IoT sensor technology plays a crucial role in the IoT tech stack because these sensors collect data from the physical world and convert it into digital signals. We think <a href="https://iot-analytics.com/5-iot-sensor-technologies/">their article</a> is worth a read.</p>
<p><a href="https://flows.nodered.org/node/node-red-contrib-string"><strong>Custom Node Spotlight - node-red-contrib-string</strong></a>
String manipulation is the bread and butter of so many programming tasks. Node-RED has a lot of tools to help you edit your strings including support for <a href="https://jsonata.org/">JSONata</a> and using the trusty function node. For those of us who prefer to keep things 'no-code' <a href="https://flows.nodered.org/node/node-red-contrib-string">node-red-contrib-string</a> allows you to stack string manipulations together quickly and easily. It has a huge library of ready to use functions.</p>
<p><a href="https://flowfuse.com/team/"><strong>FlowFuse Team News</strong></a><br />
We’d like to welcome two new members to the FlowFuse team. <a href="https://twitter.com/ianskerrett">Ian Skerrett</a> has joined as our Head of Marketing and <a href="https://www.linkedin.com/in/tracyanthonyfernandez/">Tracy Anthony</a> has joined as our HR Manager. FlowFuse continues to grow and we are becoming a truly global team.</p>
<p>Would you like to work for FlowFuse? We are currently recruiting <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004">NodeJS Developers</a> to join our team. You can view any of the roles we currently have open and apply on our <a href="https://boards.greenhouse.io/flowfuse">Jobs page</a>.</p>
<p><a href="https://app.flowfuse.com/account/create?code=FF12"><strong>Try FlowFuse for Free</strong></a><br />
As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow <a href="https://app.flowfuse.com/account/create?code=FF12">this link</a> or use the code FF12 when on the payment page after creating a new team. As an open source project you can also use <a href="https://flowfuse.com/docs/install/">FlowFuse</a> for free, forever.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/FlowFuse 1.2 is now available with single sign on and persistent context storageOur final release for 2022 with some great new features to try out2022-12-22T18:00:00ZRob Marcer<p>Control access to FlowFuse using single sign-on and retain context values when restarting projects.</p>
<!--more-->
<p>We're pleased to announce version 1.2 is now available! The latest release of the FlowFuse application contains new features, improvements, better documentation, and bug fixes.</p>
<p>We've put a great deal of work in this release to make it easier to run your own self-managed instance of FlowFuse. That includes significant improvements for FlowFuse in <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a> and <a href="https://flowfuse.com/docs/install/docker/">Docker</a>.</p>
<p>Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/226">Single Sign-On</a> Single sign-on (SSO) is a method of authentication that allows a user to access multiple applications or systems with a single set of login credentials, improving security, productivity, and user experience, and reducing IT overhead. We've implemented SSO using the Security Assertion Markup Language <a href="https://en.wikipedia.org/wiki/Security_Assertion_Markup_Language">(SAML)</a> framework. This allows users of FlowFuse Cloud, Premium and the open source edition to easily access their FlowFuse projects.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/212">Persistent Context</a> Node-RED provides a way to store information that can be shared between different nodes and flow executions without using the messages that pass through a flow. This is called ‘context’. You can now select if context should be stored in memory or in persistent storage. Persistent storage allows the stored values to be recalled after a restart of your project. You can see a demonstration of this feature on our <a href="https://youtu.be/ma2vYrXmssc">Youtube channel</a>.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#improvements"></a> Improvements</h2>
<ul>
<li>
<p>In FlowFuse 1.1 we added logging of user actions. In 1.2 we’ve <a href="https://github.com/FlowFuse/flowfuse/issues/517">improved the audit log interface</a> to help you read the recorded user actions.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/audit-log-LCeMmtLjRn-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/audit-log-LCeMmtLjRn-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="An image of the new audit log interface of FlowFuse" alt=""An image of the new audit log interface of FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/audit-log-LCeMmtLjRn-650.jpeg" width="650" height="342" /></picture></p>
</li>
<li>
<p>Configuring DNS for FlowFuse has historically been challenging as for most FlowFuse installs you'll need two entries. One for the FlowFuse application, and one for the Node-RED projects. There's been updates to the documentation to make it much easier to set this up, and much faster. Please checkout the new <a href="https://flowfuse.com/docs/install/dns-setup/">documentation</a>.</p>
</li>
<li>
<p>We've updated our documentation to always link to the latest build (older builds are still available).</p>
</li>
<li>
<p>Previously customers were asked to build their own containers for the main FlowFuse applications, as well as the Node-RED ones. For the Node-RED containers this allows customers to pre-install packages in the container they intent to use. For FlowFuse Cloud these containers are build by FlowFuse. These containers are now published to the <a href="https://hub.docker.com/u/flowforge">Docker Hub</a>. This makes it much easier to get up and running with your first containers.</p>
</li>
<li>
<p>We are now pushing our Docker builds to Docker Hub, this saves users from having to build the Docker images when installing or updating. These containers are used by default by <code>docker-compose</code>.</p>
</li>
<li>
<p>Setting up MQTT for inter-project communication and communication with devices has been simplified. Please read the improved the documentation around configuration of <a href="https://github.com/FlowFuse/flowfuse/issues/1397">MQTT</a>.</p>
</li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li>
<p>Unable to edit 'Prevent Install of External nodes' template option <a href="https://github.com/FlowFuse/flowfuse/issues/1376">#1376</a></p>
</li>
<li>
<p>Self-managed FlowFuse needs an external email server to deliver email to users. FlowFuse should be able deal with the email server being offline and gracefully recover once it is back online. <a href="https://github.com/FlowFuse/flowfuse/issues/1159">#1159</a></p>
</li>
<li>
<p>Duplicate Activity Log for Project whose state is in flight <a href="https://github.com/FlowFuse/flowfuse/issues/1461">#1461</a></p>
</li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<p><a href="https://github.com/flecoufle">flecoufle</a> for their work on <a href="https://github.com/FlowFuse/docker-compose/pull/59">#59</a></p>
<p><a href="https://github.com/sumanpaikdev">sumanpaikdev</a> for their work on <a href="https://github.com/FlowFuse/docker-compose/pull/53">#53</a></p>
<p><a href="https://github.com/sdirosa">sdirosa</a> for their work on <a href="https://github.com/FlowFuse/flowfuse/pull/1326">#1326</a></p>
<p>As an open-source project, we welcome community involvement in what we're building.
If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#try-it-out"></a> Try it out</h3>
<p>In 1.2 we've continued to improve the experience of running your own self managed FlowFuse installation. We're confident you can have self managed FlowFuse running locally in under 30 minutes.
You can install our <a href="https://flowfuse.com/docs/contribute/local/">local build</a>, through <a href="https://flowfuse.com/docs/install/docker/">Docker</a>, or <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>.</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create?code=FF12">Sign up for FlowFuse Cloud</a>
with the coupon <strong>FF12</strong> to get your first project free for a month.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.2. To use
persistent context you'll need to upgrade your projects stack.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-2-0-released/#getting-help"></a> Getting help</h3>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<p>You can raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a>.</p>
<p>We've also added a live chat widget to our website, you can access it using the icon on the bottom right corner of our website. We'd love to hear from you.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/what-flowforge-adds-to-node-red/Why you need FlowFuse when you already have Node-RED?FlowFuse makes developer collaboration, flow deployment, and scaling of infrastructure easy when working with Node-RED2022-12-15T00:00:00ZRob Marcer<p>Many organizations face challenges in IT/OT 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.</p>
<!--more-->
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>We would be happy to talk to you more about how FlowFuse can help you get the most value from Node-RED. Please <a href="https://flowfuse.com/contact-us/">contact us</a> to learn more.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-joins-openjs-foundation/FlowFuse Inc. becomes a member of the OpenJS FoundationSupporting the foundation that has given Node-RED a great home2022-12-13T12:00:00ZNick O'Leary<p>We're pleased to share the news we've joined the OpenJS Foundation to bolster our
support for the Node-RED community.</p>
<!--more-->
<p>One of our <a href="https://flowfuse.com/handbook/company/principles/#open-source-stewardship">founding principles</a> is
the importance of Open Source being at the core of what we do.</p>
<p>Node-RED has been part of the <a href="https://openjsf.org/">OpenJS Foundation</a> since it was formed in 2019.
The Foundation provides the project a vendor-neutral home and enables its open governance model that
allows anyone to get involved.</p>
<p>It also provides some vitally important resources to the community, such as the hosting for the
<a href="https://discourse.nodered.org/">community forum</a>, which regularly gets over 1.5 Million page views a
month.</p>
<p>With my Node-RED Project Lead hat on, I regularly see the impact the foundation has had on the growth
of the Node-RED community. These are things that may not always be obvious to our end users, but
we wouldn't be where we are today with them.</p>
<p>The success of what we're building with FlowFuse relies on a strong and healthy Node-RED community.
Becoming members of the Foundation allows us to provide more yet more support to Node-RED and the wider
JavaScript communities represented by the foundation.</p>
<p>For more information about this news, check out the <a href="https://openjsf.org/announcement/2022/12/13/welcoming-flowforge-to-the-openjs-foundation/">OpenJS Foundation blog post</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-1-2-released/FlowFuse 1.1.2 releasedRelease includes a fix for installing additional nodes into Node-RED.2022-12-09T12:00:00ZNick O'Leary<p>We've published a maintenance fix with an important fix for the Palette Manager in Node-RED.</p>
<!--more-->
<p>A <a href="https://github.com/FlowFuse/flowfuse/issues/1367">bug</a> was reported this week
where a user was unable to install additional nodes into their Node-RED project
using the editor's palette manager.</p>
<p>We tracked it down to an issue that was introduced in the 1.1 release, where the
project template lets you list nodes that should be blocked from being installed.
It was interpreting an empty list to mean <em>disallow everything</em>! Not quite the
intended behaviour.</p>
<p>Whilst tracking this down, we also spotted a bug (<a href="https://github.com/FlowFuse/flowfuse/issues/1379">#1379</a>)
around editing this same setting that made it tricky to work around without this
release being published.</p>
<p>These issues have now been fixed and FlowFuse 1.1.2 published.</p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-1-2-released/#bug-fixes"></a> Bug Fixes</h2>
<p>In additional to the above issues, this release includes some further fixes around
the docker and helm components:</p>
<ul>
<li>Fix fileStore hostname by @flecoufle in <a href="https://github.com/FlowFuse/docker-compose/pull/59">#59</a></li>
<li>Fix healthcheck <a href="https://github.com/FlowFuse/docker-compose/pull/62">#62</a> <a href="https://github.com/FlowFuse/docker-compose/pull/63">#63</a> <a href="https://github.com/FlowFuse/helm/pull/74">#74</a></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-1-2-released/#contributors"></a> Contributors</h2>
<p>We'd like to thank the following people for their contributions to this release:</p>
<p><a href="https://github.com/flecoufle">flecoufle</a> for their work on <a href="https://github.com/FlowFuse/docker-compose/pull/59">#59</a></p>
<p>As an open-source project, we welcome community involvement in what we're building.
If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-1-2-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>This release has already been rolled out to <a href="https://app.flowfuse.com/">FlowFuse Cloud</a>.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a></p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-1-1-2-released/#getting-help"></a> Getting help</h3>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform, please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<p>You can also raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/Configure FlowFuse in Docker to secure all trafficUse Let's Encrypt and Acme Companion to quickly set up FlowFuse to encrypt all traffic2022-12-09T00:00:00ZRob Marcer<p>Following on from our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/">previous article</a> in which we covered how to run FlowFuse in Docker on Google’s Cloud Platform, today we are going to look at how to secure HTTP traffic to your FlowFuse server.</p>
<!--more-->
<h3 id="introduction" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/#introduction"></a> Introduction</h3>
<p>When we wrote the first part of this series FlowFuse didn't have an easy path to secure HTTP traffic. Happily, two versions of FlowFuse later and at least partially inspired by these blogs, we have added the configuration you need in Docker to use HTTPS with minimal work.</p>
<p>That addition makes our job of explaining this setup a lot easier, credit to our developers for seeing the value of having an easy implementation of HTTPS in FlowFuse as part of our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/">1.0 build</a>.</p>
<p>To achieve secure HTTPS traffic we are employing a great service called <a href="https://letsencrypt.org/">Let's Encrypt</a>. In their own words, "Let’s Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit". In practice Let's Encrypt will allow us to easily secure HTTPS traffic.</p>
<p>We are also utilising a Docker image called <a href="https://github.com/nginx-proxy/acme-companion">acme-companion</a> which makes the configuration of Let's Encrypt a breeze. To quote the project's own Github page "acme-companion is a lightweight companion container for nginx-proxy. It handles the automated creation, renewal and use of SSL certificates for proxied Docker containers through the ACME protocol". It's a great project and credit to the team over there for making it a lot easier to secure the internet.</p>
<p>Now we've covered our goals and the tools we are going to use let's configure our existing GCP VM to secure all web traffic.</p>
<h3 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/#prerequisites"></a> Prerequisites</h3>
<p>As mentioned above, you will need to be running FlowFuse version 1.0 or higher to follow this guide. If you are using an older version you can upgrade now using the <a href="https://flowfuse.com/docs/upgrade/">instructions here</a>.</p>
<h3 id="update-docker-compose" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/#update-docker-compose"></a> Update Docker Compose</h3>
<p>The first step is to edit our Docker compose file. We're using Nano again to edit files so we will run this command:</p>
<pre><code>sudo nano /opt/flowforge/docker-compose-1.1.1/docker-compose.yml
</code></pre>
<p>In the docker-compose.yml file, un-comment the following lines:</p>
<div style="position: relative" id="code-container-38">
<pre class="language-yaml"><code id="code-38" class="language-yaml"><span class="token punctuation">-</span> <span class="token string">"./certs:/etc/nginx/certs"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-38" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-39">
<pre class="language-yaml"><code id="code-39" class="language-yaml"><span class="token punctuation">-</span> <span class="token string">"443:443"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-39" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-40">
<pre class="language-yaml"><code id="code-40" class="language-yaml"> <span class="token key atrule">acme</span><span class="token punctuation">:</span><br /> <span class="token key atrule">image</span><span class="token punctuation">:</span> nginxproxy/acme<span class="token punctuation">-</span>companion<br /> <span class="token key atrule">volumes</span><span class="token punctuation">:</span><br /> <span class="token punctuation">-</span> <span class="token string">"/var/run/docker.sock:/var/run/docker.sock:ro"</span><br /> <span class="token punctuation">-</span> <span class="token string">"./acme:/etc/acme.sh"</span><br /> <span class="token key atrule">volumes_from</span><span class="token punctuation">:</span><br /> <span class="token punctuation">-</span> nginx<span class="token punctuation">:</span>rw<br /> <span class="token key atrule">environment</span><span class="token punctuation">:</span><br /> <span class="token punctuation">-</span> <span class="token string">"DEFAULT_EMAIL=mail@example.com"</span><br /> <span class="token key atrule">depends_on</span><span class="token punctuation">:</span><br /> <span class="token punctuation">-</span> <span class="token string">"nginx"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-40" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>We should also redirect all traffic to use HTTPS, to do that un-comment the following in the nginx service section:</p>
<div style="position: relative" id="code-container-44">
<pre class="language-yaml"><code id="code-44" class="language-yaml"><span class="token key atrule">Environment</span><span class="token punctuation">:</span><br /> <span class="token punctuation">-</span> <span class="token string">"HTTPS_METHOD=redirect"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-44" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>We now need to add the configuration for LetsEncrypt, edit the following lines to include a valid email address and the correct domain for where you are hosting your FlowFuse server:</p>
<div style="position: relative" id="code-container-48">
<pre class="language-yaml"><code id="code-48" class="language-yaml"><span class="token punctuation">-</span> <span class="token string">"DEFAULT_EMAIL=mail@example.com"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-48" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-49">
<pre class="language-yaml"><code id="code-49" class="language-yaml"><span class="token punctuation">-</span> <span class="token string">"LETSENCRYPT_HOST=mqtt.example.com"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-49" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<div style="position: relative" id="code-container-50">
<pre class="language-yaml"><code id="code-50" class="language-yaml"><span class="token punctuation">-</span> <span class="token string">"LETSENCRYPT_HOST=forge.example.com"</span></code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-50" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key.</p>
<h3 id="update-flowforge.yml" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/#update-flowforge.yml"></a> Update flowforge.yml</h3>
<p>Next, we need to edit the public_url for the MQTT broker:</p>
<pre><code>sudo nano /opt/flowforge/docker-compose-1.1.1/docker-compose.yml
</code></pre>
<p>Then replace ws:// with wss://</p>
<div style="position: relative" id="code-container-64">
<pre class="language-yaml"><code id="code-64" class="language-yaml"><span class="token key atrule">public_url</span><span class="token punctuation">:</span> wss<span class="token punctuation">:</span>//mqtt.flowforge<span class="token punctuation">-</span>demo.com</code></pre>
<button class="code-copy mdi mdi-content-copy" data-clipboard-target="#code-64" style="position: absolute; top: 7.5px; right: 6px; padding-top: 3px; cursor: pointer; outline: none; opacity: 0.8;" title="Copy">
<span style="display:inline-block;background:url() no-repeat center center / contain;background: initial" class=""></span>
</button>
</div>
<p>Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key.</p>
<h3 id="restart-your-docker-containers" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/flowforge-gcp-https-set-up/#restart-your-docker-containers"></a> Restart your Docker containers</h3>
<p>OK, we should be ready to restart the Docker containers, run the command:</p>
<pre><code>sudo docker compose -p flowforge up -d
</code></pre>
<p>If you reload your FlowFuse root directory in a web browser you should now see that your traffic is encrypted using LetsEncypt.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/https-working-JYJ_qo7R2Z-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/https-working-JYJ_qo7R2Z-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="A screenshot from Safari web browser showing that the traffic to FlowFuse is encrypted" alt="A screenshot from Safari web browser showing that the traffic to FlowFuse is encrypted" loading="lazy" decoding="async" src="https://flowfuse.com/img/https-working-JYJ_qo7R2Z-650.jpeg" width="650" height="553" /></picture></p>
<p>Nice! That’s it, you can now access your FlowFuse installation securely.</p>
<p>In the next and final part of this series of articles, we are going to look at how we can actually use FlowFuse including how to build flows and deploy and update them on Devices linked to a project.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/create-http-trigger-with-authentication/Create HTTP triggers with authenticationFrom any browser or command line you now have the ability to securely trigger your flows2022-12-07T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>Doing so safely with authentication is slightly harder, but not a lot. FlowFuse makes it rather easy to accomplish.</p>
<h3 id="creating-the-http-flow" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/create-http-trigger-with-authentication/#creating-the-http-flow"></a> Creating the HTTP flow</h3>
<p>When you start a project on FlowFuse, remember the project name. For this how-to we’ll use <code>example</code>. 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.</p>
<p>First off; let’s set the HTTP in node properties:</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/edit-http-node-jVuc28Cc_x-512.avif 512w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/edit-http-node-jVuc28Cc_x-512.webp 512w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Shows the UI to edit the node's properties" alt="Shows the UI to edit the node's properties" loading="lazy" decoding="async" src="https://flowfuse.com/img/edit-http-node-jVuc28Cc_x-512.jpeg" width="512" height="320" /></picture></p>
<p>You can import this flow into your own project if you’d like:</p>
<pre><code>[{"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":"/http-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":[]}]
</code></pre>
<p>Now I’ve opened a terminal and executed:</p>
<pre><code>curl -X POST https://example.flowforge.com/http-trigger
</code></pre>
<p>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</p>
<h3 id="securing-the-http-trigger-with-a-username-and-password" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/create-http-trigger-with-authentication/#securing-the-http-trigger-with-a-username-and-password"></a> Securing the HTTP trigger with a username and password</h3>
<p>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!</p>
<p>Let’s validate the endpoint that worked a minute ago doesn’t anymore:</p>
<pre><code>curl -X POST https://example.flowforge.cloud/http-trigger
=> Unauthorized
</code></pre>
<p>Let’s get it working again: (replace <username> and <password> with the details from the sticky note)</password></username></p>
<pre><code>curl -X POST https://<username>:<password>@example.flowforge.cloud/http-trigger
</code></pre>
<p>That’s it! You now have a flow that’s protected by a username and password combination!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/Format your Node-RED flows for better team collaborationFrom 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 road2022-12-05T00:00:00ZRob Marcer<p>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.</p>
<!--more-->
<p>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.</p>
<h3 id="give-your-groups-descriptive-names" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#give-your-groups-descriptive-names"></a> Give your groups descriptive names</h3>
<p>Let’s start with <a href="https://nodered.org/docs/user-guide/editor/workspace/groups">grouping your flows</a> 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.</p>
<h4 id="this-is-not-helpful%2C-'time'-doesn't-tell-you-enough-to-understand-the-flow's-purpose." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-not-helpful%2C-'time'-doesn't-tell-you-enough-to-understand-the-flow's-purpose."></a> This is not helpful, 'Time' doesn't tell you enough to understand the flow's purpose.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/name-bad-r0MRGV-oz5-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/name-bad-r0MRGV-oz5-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the example of flow having the bad group name" alt=""Screenshot showing the example of flow having the bad group name"" loading="lazy" decoding="async" src="https://flowfuse.com/img/name-bad-r0MRGV-oz5-650.jpeg" width="650" height="104" /></picture></p>
<h4 id="this-is-much-better%2C-we-know-what-the-flow-is-doing-without-inspecting-the-nodes." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-much-better%2C-we-know-what-the-flow-is-doing-without-inspecting-the-nodes."></a> This is much better, we know what the flow is doing without inspecting the nodes.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/name-good-NfYokdlmdu-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/name-good-NfYokdlmdu-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the example of flow having the good group name" alt=""Screenshot showing the example of flow having the good group name"" loading="lazy" decoding="async" src="https://flowfuse.com/img/name-good-NfYokdlmdu-650.jpeg" width="650" height="98" /></picture></p>
<h3 id="explain-what-your-switches-do" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#explain-what-your-switches-do"></a> Explain what your switches do</h3>
<p>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.</p>
<h4 id="this-is-not-easy-to-understand%2C-what-does-the-switch-do%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-not-easy-to-understand%2C-what-does-the-switch-do%3F"></a> This is not easy to understand, what does the switch do?</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/switch-bad-hMtSmGSxyO-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/switch-bad-hMtSmGSxyO-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the example of flow having the switch with bad name" alt=""Screenshot showing the example of flow having the switch with bad name"" loading="lazy" decoding="async" src="https://flowfuse.com/img/switch-bad-hMtSmGSxyO-650.jpeg" width="650" height="142" /></picture></p>
<h4 id="this-is-a-lot-better%2C-we-can-see-that-the-top-debug-should-be-triggered." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-a-lot-better%2C-we-can-see-that-the-top-debug-should-be-triggered."></a> This is a lot better, we can see that the top debug should be triggered.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/switch-good-volyDrf83p-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/switch-good-volyDrf83p-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the example of flow having the switch with good name" alt=""Screenshot showing the example of flow having the switch with good name"" loading="lazy" decoding="async" src="https://flowfuse.com/img/switch-good-volyDrf83p-650.jpeg" width="650" height="118" /></picture></p>
<h3 id="where-possible-your-flows-should-work-down-the-canvas" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#where-possible-your-flows-should-work-down-the-canvas"></a> Where possible your flows should work down the canvas</h3>
<p>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.</p>
<h4 id="this-is-almost-unreadable%2C-it's-very-hard-to-work-out-the-order-of-the-groups." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-almost-unreadable%2C-it's-very-hard-to-work-out-the-order-of-the-groups."></a> This is almost unreadable, it's very hard to work out the order of the groups.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowdown-bad-zh02DeDK7C-613.avif 613w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowdown-bad-zh02DeDK7C-613.webp 613w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow that doesn't work down the canvas" alt=""Screenshot showing an example of flow that doesn't work down the canvas"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowdown-bad-zh02DeDK7C-613.jpeg" width="613" height="414" /></picture></p>
<h4 id="where-as-this-is-so-much-easier-to-understand." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#where-as-this-is-so-much-easier-to-understand."></a> Where as this is so much easier to understand.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/flowdown-good--XV3bghhxA-477.avif 477w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/flowdown-good--XV3bghhxA-477.webp 477w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow that works down the canvas" alt=""Screenshot showing an example of flow that works down the canvas"" loading="lazy" decoding="async" src="https://flowfuse.com/img/flowdown-good--XV3bghhxA-477.jpeg" width="477" height="515" /></picture></p>
<h3 id="use-link-nodes-rather-than-wires-to-join-groups" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#use-link-nodes-rather-than-wires-to-join-groups"></a> Use link nodes rather than wires to join groups</h3>
<p>Groups should not be joined using wires, it just looks untidy and quickly reduces readability of your flows.</p>
<h4 id="the-wire-is-blocking-the-title%2C-it-only-gets-worse-as-you-add-more-wires." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#the-wire-is-blocking-the-title%2C-it-only-gets-worse-as-you-add-more-wires."></a> The wire is blocking the title, it only gets worse as you add more wires.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/link-bad-0c5UXs0s41-633.avif 633w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/link-bad-0c5UXs0s41-633.webp 633w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow with wires blocking group titles" alt=""Screenshot showing an example of flow with wires blocking group titles"" loading="lazy" decoding="async" src="https://flowfuse.com/img/link-bad-0c5UXs0s41-633.jpeg" width="633" height="252" /></picture></p>
<h4 id="you-can-see-the-group-titles-easily-now." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#you-can-see-the-group-titles-easily-now."></a> You can see the group titles easily now.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/link-good-xNEmSp9xSF-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/link-good-xNEmSp9xSF-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow with link nodes improving readability" alt=""Screenshot showing an example of flow with link nodes improving readability"" loading="lazy" decoding="async" src="https://flowfuse.com/img/link-good-xNEmSp9xSF-650.jpeg" width="650" height="247" /></picture></p>
<h3 id="keep-your-groups-compact" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#keep-your-groups-compact"></a> Keep your groups compact</h3>
<p>Keeping your groups compact will save time when reading the flow. This is especially helpful if when viewed on a smaller screen.</p>
<h4 id="consider-how-hard-a-flow-made-of-groups-spaced-out-like-this-would-be-to-read-on-a-smaller-laptop-screen." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#consider-how-hard-a-flow-made-of-groups-spaced-out-like-this-would-be-to-read-on-a-smaller-laptop-screen."></a> Consider how hard a flow made of groups spaced out like this would be to read on a smaller laptop screen.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-bad-6HRfnPNsXO-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-bad-6HRfnPNsXO-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow with widely spaced groups" alt=""Screenshot showing an example of flow with widely spaced groups"" loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-bad-6HRfnPNsXO-650.jpeg" width="650" height="328" /></picture></p>
<h4 id="this-now-takes-up-less-space-and-is-arguably-easier-to-read-on-any-screen-size." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-now-takes-up-less-space-and-is-arguably-easier-to-read-on-any-screen-size."></a> This now takes up less space and is arguably easier to read on any screen size.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/compact-good-iZ_Y8ErhrM-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/compact-good-iZ_Y8ErhrM-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of flow with compact groups" alt=""Screenshot showing an example of flow with compact groups"" loading="lazy" decoding="async" src="https://flowfuse.com/img/compact-good-iZ_Y8ErhrM-650.jpeg" width="650" height="103" /></picture></p>
<h3 id="don%E2%80%99t-cross-beams-wires" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#don%E2%80%99t-cross-beams-wires"></a> Don’t cross <s>beams</s> wires</h3>
<p>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.</p>
<h4 id="this-is-confusing%2C-which-change-node-does-the-top-switch-output-link-to%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-confusing%2C-which-change-node-does-the-top-switch-output-link-to%3F"></a> This is confusing, which change node does the top switch output link to?</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/wires-bad-lzzHDQMS8t-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/wires-bad-lzzHDQMS8t-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of the flow with nodes having crossed beams/wires" alt=""Screenshot showing an example of the flow with nodes having crossed beams/wires"" loading="lazy" decoding="async" src="https://flowfuse.com/img/wires-bad-lzzHDQMS8t-650.jpeg" width="650" height="103" /></picture></p>
<h4 id="this-is-better%2C-much-less-chance-of-confusing-the-change-nodes." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-better%2C-much-less-chance-of-confusing-the-change-nodes."></a> This is better, much less chance of confusing the change nodes.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/wires-good-PQ2GBOwjuh-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/wires-good-PQ2GBOwjuh-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of the flow with nodes having correctly linked beams/wires" alt=""Screenshot showing an example of the flow with nodes having correctly linked beams/wires"" loading="lazy" decoding="async" src="https://flowfuse.com/img/wires-good-PQ2GBOwjuh-650.jpeg" width="650" height="104" /></picture></p>
<h3 id="don%E2%80%99t-use-link-nodes-in-groups-where-avoidable" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#don%E2%80%99t-use-link-nodes-in-groups-where-avoidable"></a> Don’t use link nodes in groups where avoidable</h3>
<p>Excessive link nodes within groups can make a flow much harder to understand, where possible use wires to join nodes within a group.</p>
<h4 id="this-is-hard-to-read-and-you-will-end-up-checking-the-link-nodes-again-and-again." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#this-is-hard-to-read-and-you-will-end-up-checking-the-link-nodes-again-and-again."></a> This is hard to read and you will end up checking the link nodes again and again.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/groupwires-bad-FQGzfBOIY6-575.avif 575w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/groupwires-bad-FQGzfBOIY6-575.webp 575w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the example of flow having the uneccessary link nodes" alt=""Screenshot showing the example of flow having the uneccessary link nodes"" loading="lazy" decoding="async" src="https://flowfuse.com/img/groupwires-bad-FQGzfBOIY6-575.jpeg" width="575" height="235" /></picture></p>
<h4 id="functionally-identical-to-the-example-above%2C-it-should-only-take-a-few-seconds-to-understand-this-flow-now." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#functionally-identical-to-the-example-above%2C-it-should-only-take-a-few-seconds-to-understand-this-flow-now."></a> Functionally identical to the example above, it should only take a few seconds to understand this flow now.</h4>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/groupwires-good-_NUcSy-4kN-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/groupwires-good-_NUcSy-4kN-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing an example of the flow with the avoided link nodes" alt=""Screenshot showing an example of the flow with the avoided link nodes"" loading="lazy" decoding="async" src="https://flowfuse.com/img/groupwires-good-_NUcSy-4kN-650.jpeg" width="650" height="104" /></picture></p>
<h3 id="boost-collaboration-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#boost-collaboration-with-flowfuse"></a> Boost Collaboration with FlowFuse</h3>
<p><a href="https://flowfuse.com/">FlowFuse</a> 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 <a href="https://flowfuse.com/customer-stories/">customer stories</a>.</p>
<p>With FlowFuse, you can quickly <a href="https://flowfuse.com/docs/user/team/">set up and manage teams</a>, giving each member the right level of access. It keeps all your <a href="https://www.youtube.com/watch?v=KOnQnR7yfT0&list=PLpcyqc7kNgp3nRacWBJ9JUVUJqtTjXdvh&index=2">Node-RED instances organized in one place</a>, so your team can collaborate seamlessly. Plus, it features <a href="https://www.youtube.com/watch?v=m2Onip4Lf4w">snapshots</a>, 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.</p>
<p><strong><a href="https://app.flowfuse.com/account/create/">Sign up</a> now for a free trial and experience FlowFuse's powerful collaboration tools!</strong></p>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/node-red-flow-best-practice/#conclusion"></a> Conclusion</h3>
<p>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.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/12/community-news-11/Community News November 2022News from the FlowFuse and Node-RED communities2022-12-02T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter for November 2022, a monthly roundup of what’s been happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you think we should share on our newsletters please <a href="mailto:contact@flowfuse.com/">get it touch</a>.</p>
<p><a href="https://github.com/node-red/node-red/milestone/19"><strong>Node-RED Nears 3.1 Release</strong></a>
As we mentioned last month, the release of Node-RED 3.1 is expected very soon. 3.1 includes lots of great new features such as support for <a href="https://github.com/node-red/node-red/pull/3938">locking flows in the editor</a> and <a href="https://github.com/node-red/node-red/pull/3930">improving the user experience around hiding flows</a>. As an open source project the development of Node-RED is entirely dependent on individuals and companies giving their time to work towards each new release. If you'd like to know how you can get involved you can read more on the <a href="https://nodered.org/about/contribute/">Node-RED web site</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/"><strong>FlowFuse 1.1 Released</strong></a><br />
Version 1.1 of FlowFuse was released on 24th November. Our latest release included some great new features such as <a href="https://github.com/FlowFuse/flowfuse/issues/998">Persistent file storage</a>, the ability to <a href="https://flowfuse.com/docs/migration/node-red-tools/">import Node-RED snapshots</a> from outside of FlowFuse and a much improved interface to <a href="https://github.com/FlowFuse/flowfuse/issues/1046">deploy projects to your devices</a>. We're now working towards our final release of 2022 which is due just before Christmas. You can see what we are planning to deliver in that release and beyond on <a href="https://github.com/orgs/FlowFuse/projects/5">FlowFuse's project board</a>.</p>
<p>If you’d like to learn more about what else was included in 1.1 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/">blog post</a>, <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v1.1.0">GitHub release page</a>, and <a href="https://www.youtube.com/watch?v=134iljE_urI">Youtube channel</a>.</p>
<p><a href="https://github.com/playfultechnology/node-redscape"><strong>Node-Redscape - 100% Free, Open-Source Escape Room Control Software</strong></a><br />
As we'll come to later in this newsletter, FlowFuse visited an Escape Room in Winchester as part of our team meet-up. Co-incidentally, a great Node-RED project came up a few days after our visit which we thought was worth sharing. In their own words <a href="https://github.com/playfultechnology/node-redscape">Node-Redscape</a> 'provides a set of templates, flows, and examples that turn Node-RED into a complete Escape Room automation system'. Very topical for us and also seems like a great project. You can learn more about the project on <a href="https://www.youtube.com/watch?v=f9yYDxqK_2E">Youtube</a> as well as on <a href="https://github.com/playfultechnology/node-redscape">Github</a>.</p>
<p><strong>FlowFuse team meetup</strong>
FlowFuse is a fully remote team, we currently have a strong skew towards western Europe but we are in the process of adding team members in each of the continents. On that point, any great Product Managers who live in Antarctica are encouraged to <a href="https://boards.greenhouse.io/flowfuse/jobs/4717778004">apply for a job with us</a>! Remote work is great but it's also valuable to get everyone together in the real world from time to time. FlowFuse had such a meet up last month in Winchester, UK. We came from near and far and took the opportunity to have productive round-table discussions about some features we are working towards in 1.2 and beyond. We also dropped into <a href="https://cluecapers.co.uk/">Clue Capers</a>, a great Escape Room in the center of Winchester who provided the photo below to mark the occasion.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/clue-capers-vokh-bWOum-650.avif 650w, https://flowfuse.com/img/clue-capers-vokh-bWOum-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/clue-capers-vokh-bWOum-650.webp 650w, https://flowfuse.com/img/clue-capers-vokh-bWOum-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/clue-capers-vokh-bWOum-650.jpeg 650w, https://flowfuse.com/img/clue-capers-vokh-bWOum-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="The FlowFuse team pictured during our visit to Clue Capers" alt=""The FlowFuse team pictured during our visit to Clue Capers"" loading="lazy" decoding="async" src="https://flowfuse.com/img/clue-capers-vokh-bWOum-650.jpeg" width="1300" height="886" /></picture></p>
<p><a href="https://flowfuse.com/team/"><strong>FlowFuse Team News</strong></a><br />
We are currently recruiting a <a href="https://boards.greenhouse.io/flowfuse/jobs/4717778004">Product Manager</a>, and a <a href="https://boards.greenhouse.io/flowfuse/jobs/4700809004">Senior Community Manager</a>. You can view any of the roles we currently have open and apply on our <a href="https://boards.greenhouse.io/flowfuse">Jobs page</a>.</p>
<p><a href="https://app.flowfuse.com/account/create?code=RELEASE11"><strong>Try FlowFuse for Free</strong></a><br />
As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow <a href="https://app.flowfuse.com/account/create?code=RELEASE11">this link</a> or use the code RELEASE11 when on the payment page after creating a new team. As an open source project you can also use <a href="https://flowfuse.com/docs/install/">FlowFuse</a> for free, forever.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/Challenges scaling Node-RED with DIY toolingNode-RED is very easy to get up and running for your first instance but what about your 100th?2022-11-30T00:00:00ZZJ van de Weg<p>In this post, I'm going to share some of the challenges customers face when
scaling Node-RED with Do-It-Yourself tooling.</p>
<!--more-->
<p>Specifically, we'll talk about
common threads in their journey building their own tooling around Node-RED, its
flows, and deploying them. Node-RED is a visual programming environment for
wiring together hardware devices, APIs and online services in a single
application. It's great because it's flexible enough to be used by both
beginners and experts alike; however, going from one instance of Node-RED to 100
isn't for the faint-hearted.</p>
<h3 id="zero-to-hero-in-a-few-days" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/#zero-to-hero-in-a-few-days"></a> Zero to hero in a few days</h3>
<p>If you’re new to Node-RED and are just getting started, the first step is simple:
go to <a href="https://nodered.org/">nodered.org</a>, download, install and run. There’s no
need to configure anything or set up credentials or security or alerts. You can
get started with Node-RED straight away by simply running it on your machine.
The guides and scripts provided on Node-RED or are more than enough to get
started. You'll dive right in and start developing your flows.</p>
<h3 id="onto-the-second-instance" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/#onto-the-second-instance"></a> Onto the second instance</h3>
<p>The next instance is a simple copy of the first. You'll need to make sure that
you are installing the same version on both instances, and that any
configuration files (such as the <code>settings.js</code>) are also in sync.</p>
<p>The second instance improves the first equally. Docs are read again,
improvements are made, and copied over. Life's good! Although there's a slight
itch to start automating the setup, it's ignored. Just too many open questions
on how to achieve it: write scripts in bash? Or can we use Node-RED to manage
Node-RED? Automation can wait, there's new flows to implement!</p>
<h3 id="wake-up-call%3B-how-many-of-them-do-we-have%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/#wake-up-call%3B-how-many-of-them-do-we-have%3F"></a> Wake-up call; how many of them do we have?</h3>
<p>There's nothing like a good ol' wake-up call to get you back on track. One of
our team members asked a questions about a buggy flow you've got no recollection
of. During the investigation there's issues left and right; it's running a much
older version, missing standard packages, the settings are out of date, and the
timezone not set to UTC? Adoption of Node-RED is going quite well, it's useful
and effective. Gets the job done without fuss. But now there's toil in
maintaining them; ensure the right tooling is build, properly documented, it
needs dashboards and overviews, lots of work to be done. Not quite business
related, but Node-RED is important for the company, the bosses sure would
approve spending 2 months building these tools!</p>
<p>But then something happened—there was a higher priority project to be picked up.
Some tooling could be written for a couple of hours a month, but not two
dedicated months to get it in tip top shape. Better than nothing! Built a
dashboard in Node-RED to keep track of all devices, there's some scripts and a
few flows that aid in monitoring and maintenance. Scaling further is possible,
but confidence in the tooling isn't sky high.</p>
<h3 id="data-extraction-at-scale%3B-now-there's-over-100" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/#data-extraction-at-scale%3B-now-there's-over-100"></a> Data extraction at scale; now there's over 100</h3>
<p>At <a href="https://flowfuse.com/">FlowFuse</a> we've got regular conversations with customers managing 100s of
devices. Scaling to that many devices and runtimes requires hours of development
each week alongside monitoring, maintenance, and auditing.</p>
<p>As a side effect of investing more time into Node-RED and its ecosystem the
organization has developed a few standards. Standard custom nodes that are
pre-installed (👋 <code>moment.js</code>), a style guide published for developing flows,
maybe even flow linting: https://github.com/node-red/nrlint</p>
<p>The security model is fairly decent. One just hopes the
<a href="https://nl.wikipedia.org/wiki/Chief_Information_Security_Officer">CISO</a> doesn't
inspects them, but it's a fair bet they won't; we're far away from the
headquarters, right?</p>
<p>Many other edge cases itch in the back of our heads, but we can't focus on those right now.</p>
<h3 id="conclusion" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/scaling-node-red-with-diy-tooling/#conclusion"></a> Conclusion</h3>
<p>Node-RED is a great tool, it's got many built-in features that make it easy to
get started with no coding experience necessary. Running it at scale, in a
production environment can require a lot of sys-ops and dev-ops time and we
think <a href="https://flowfuse.com/">FlowFuse</a> is a great solution to keep that admin and tech debt in check.</p>
<p>If you're running into these challenges we believe we can help, you can adopt
our <a href="https://flowfuse.com/docs/install/">free and open source edition</a>.</p>
<p>Additionally, to get a head start or enhance your current setup, explore our <a href="https://flowfuse.com/ebooks/beginner-guide-to-a-professional-nodered/">Beginner's Guide to Professional Node-RED</a>. This comprehensive ebook offers a clear overview of Node-RED’s capabilities and practical tips for making the most of its features.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/respin-docker-compose-01/Re-spin of Docker-Compose install packageAdding ability to locally build file-server container2022-11-25T00:00:00ZBen Hardill<p>After <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/">yesterdays 1.1.0 FlowFuse release</a> we noticed a few minor issues with the docker-compose install instructions.</p>
<p>We had relied on the fact we are now publishing container images to Docker Hub to install the new <code>flowforge/file-server</code> container. At this time we are only building images for amd64 and arm64. Further, it was only tagged them with the current release number.</p>
<p>To remedy this we tagged v1.1.1 of the docker-compose project in which we have included the required <code>Dockerfile</code> and resources to build the <code>flowforge/file-server</code> locally, updated the <code>build-containers.sh</code> script to build this container. We will also build the published containers for armv7 and include a latest tag going forward.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/FlowFuse 1.1 released with persistent file storageA new set of features to improve FlowFuse as the best solution for running Node-RED in production in a secure, scalable, and team-based environment.2022-11-24T18:00:00ZRob Marcer<p>Persist files on your FlowFuse Projects, publish locally developed flows to dozens of Devices in a few clicks, and use our new interface for managing Project Deployments.</p>
<!--more-->
<p>We're pleased to announce version 1.1 is now available! The latest release of the FlowFuse application contains new features, many improvements, and bug fixes. Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/998">Persistent File Storage</a> We've had a great deal of feedback
from our customers that being able to persist files in a project is a vital feature
in Node-RED. In FlowFuse 1.1 flows can now create and persist files within
your Projects. We know those files are used in many creative ways and we're looking
forward to seeing how users improve their Projects using this new feature.</p>
<p><a href="https://flowfuse.com/docs/migration/node-red-tools/">Import Snapshots from Outside FlowFuse</a> Developers may wish to
work on Node-RED in a local environment but want an easy path to share that with their team. You can now link your Node-RED instances running outside of FlowFuse and push Snapshots directly into your FlowFuse Projects to leverage FlowFuse fully. With this new feature we've made it effortless to push a local build of a project to FlowFuse for deployment to your staging and production FlowFuse instances.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#improvements"></a> Improvements</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/1046">Project Deployments UX</a>
We've reworked the interface for managing your FlowFuse Deployments of Node-RED.
We are seeing FlowFuse users deploying their Projects to edge devices at scale.
This is another step towards making it easier for users to manage a large quantity
of devices in their Projects.</p>
<p>When users change their username, email address, or password they'll now be
notified through email of changes to ensure they were made by the
user.</p>
<p>In this release a lot of effort went into the install process, specifically the
local install method. First and foremost; a default Stack and Template will be
installed automatically. That will ensure users get up and running with
Node-RED more quickly. Administrators of each platform can still change Stacks
and Templates when needed. Secondly, the installer now auto generates the
configuration file for Mosquitto, the MQTT broker FlowFuse uses. This again should save
administrators time when installing FlowFuse.</p>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#bug-fixes"></a> Bug Fixes</h2>
<p>The v1.0.1 release included a bug fix where <a href="https://github.com/FlowFuse/flowfuse/issues/1186">snapshot rollbacks</a>
didn't work, which has also been included in v1.1 onwards.</p>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li>
<p>When installing the stack during a FlowFuse installation the process would quit on Windows <a href="https://github.com/FlowFuse/installer/issues/62">#62</a></p>
</li>
<li>
<p>After accepting an invite to join a team, users are no longer seeing a blank page <a href="https://github.com/FlowFuse/flowfuse/issues/1208">#1208</a></p>
</li>
<li>
<p>Pagination on device deployments wasn't showing all devices <a href="https://github.com/FlowFuse/flowfuse/issues/1207">#1207</a></p>
</li>
<li>
<p>Markdown rendering when selecting the project type wasn't quite working, fixed now! <a href="https://github.com/FlowFuse/flowfuse/issues/1171">#1171</a></p>
</li>
<li>
<p>Continuous spinner in UI body when entering a new (short) password <a href="https://github.com/FlowFuse/flowfuse/issues/1280">#1280</a></p>
</li>
<li>
<p>Friendly stack name not shown in the Change Project Stack option list <a href="https://github.com/FlowFuse/flowfuse/issues/1169">#1169</a></p>
</li>
<li>
<p>The stacks view in the admin area didn't render properly <a href="https://github.com/FlowFuse/flowfuse/issues/1260">#1260</a></p>
</li>
<li>
<p>Like the deployments page, pagination for stacks was broken. <a href="https://github.com/FlowFuse/flowfuse/issues/1164">#1164</a></p>
</li>
<li>
<p>Several UX and UI bugs got polished away!</p>
</li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<p><a href="https://github.com/mikermcneil">mikermcneil</a> for their work on <a href="https://github.com/FlowFuse/flowfuse/pull/1301">#1301</a></p>
<p>As an open-source project, we welcome community involvement in what we're building.
If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#try-it-out"></a> Try it out</h3>
<p>As said before, a lot of effort went into the local installer. We're confident
you can have your own FlowFuse running locally in about 30 minutes.
<a href="https://flowfuse.com/docs/contribute/local/">Get started right away!</a>
(<a href="https://flowfuse.com/docs/install/docker/">Docker</a> and <a href="https://flowfuse.com/docs/install/kubernetes/">Kubernetes</a>
are available too!)</p>
<p>If you'd rather use our hosted offering: <a href="https://app.flowfuse.com/account/create?code=RELEASE11">Sign up for FlowFuse Cloud</a>
with the coupon <strong>RELEASE11</strong> to get your first project free for a month.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 1.1. To use
persisted files you'll need to upgrade your projects stack. You'll be prompted
to do so on the project page.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading your FlowFuse instance</a></p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/flowforge-1-1-released/#getting-help"></a> Getting help</h3>
<p>Please check FlowFuse's <a href="https://flowfuse.com/docs/">documentation</a> as the answers to many questions are covered there.</p>
<p>If you hit any problems with the platform please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Chat with us on the <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<p>You can also raise a support ticket by emailing <a href="mailto:support@flowfuse.com/">support@flowfuse.com</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/11/community-news-10/Community News October 2022News from the FlowFuse and Node-RED communities2022-11-04T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter for October 2022, a monthly roundup of what’s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/"><strong>FlowFuse 1.0 Released</strong></a><br />
Version 1.0 was released on 27th October. Our latest release represents our vision of the base set of features needed for you to get great value from using FlowFuse in a production environment. That's not to say we are done, we will continue to add features, improve our interfaces and fix bugs with the same enthusiasm as we've worked towards 1.0. We'd like to hear your feedback on what we will be including in <a href="https://github.com/orgs/FlowFuse/projects/5">1.1 and beyond</a>.</p>
<p>If you’d like to learn more about what else was included in 1.0 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/">blog post</a>, on our <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v1.0.0">GitHub release page</a> and on our <a href="https://www.youtube.com/watch?v=5TLT7CQR7iI">Youtube channel</a>. We’d also love for more of you to get involved in the development of FlowFuse, <a href="https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md">contributions to the code</a> and <a href="https://github.com/FlowFuse/flowfuse/issues">bug reports</a> are really appreciated.</p>
<p><a href="https://github.com/node-red/node-red/milestone/19"><strong>Node-RED Nears 3.1 Release</strong></a>
The next release of Node-RED has some great new features including support for <a href="https://github.com/node-red/node-red/pull/3938">locking flows in the editor</a> and <a href="https://github.com/node-red/node-red/pull/3930">improving the user experience around hiding flows</a>. We'd expect the first 3.1 beta to be available in November with a full release following shortly afterwards.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/seed-round-bring-node-red-to-enterprise/"><strong>FlowFuse raises $7.25M to bring Node-RED to the Enterprise</strong></a><br />
Earlier this week, we announced a $7.25M seed round led by <a href="https://www.cotacapital.com/knowledgecapital/flowforge-closes-the-gap-between-it-and-ot">Cota Capital</a>, joined by Westwave Capital, Uncorrelated Ventures, and Open Core Ventures. This brings a huge amount of extensive knowledge and experience in IoT, open source, enterprise-ready software solutions. You can read more about what this investment means for FlowFuse in this <a href="https://techcrunch.com/2022/11/03/flowforge-nabs-7-2m-to-help-companies-integrate-iot-using-node-red">TechCrunch article</a>.</p>
<p><a href="https://stevesnoderedguide.com/node-red-dashboard"><strong>Node-RED Dashboard - Beginners Guide</strong></a><br />
It's great to see members of the Node-RED community taking their personal time to help us all build better projects. This write up takes you through the basics of creating your first Dashboard through to more advanced techniques to help your interfaces look professional and provide great user experiences.</p>
<p><a href="https://flowfuse.com/team/"><strong>FlowFuse Team News</strong></a><br />
We are currently recruiting <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004">NodeJS Developers</a>, a <a href="https://boards.greenhouse.io/flowfuse/jobs/4717778004">Product Manager</a>, a <a href="https://boards.greenhouse.io/flowfuse/jobs/4687876004">Recruiter / PeopleOps Manager</a> and a <a href="https://boards.greenhouse.io/flowfuse/jobs/4700809004">Senior Community Manager</a>. You can view any of the roles we currently have open and apply on our <a href="https://boards.greenhouse.io/flowfuse">Jobs page</a>.</p>
<p>We'd also like to welcome <a href="https://github.com/Pezmc">Pez Cuckow</a> who joined FlowFuse as a Senior Software Engineer in October.</p>
<p><a href="https://app.flowfuse.com/account/create?code=RELEASE1"><strong>Try FlowFuse for Free</strong></a><br />
As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on our managed FlowFuse platform when you create a new team. To get this discount please follow <a href="https://app.flowfuse.com/account/create?code=RELEASE010">this link</a> or use the code RELEASE1 when on the payment page after creating a new team. As an open source project you can also use <a href="https://flowfuse.com/docs/install/">FlowFuse</a> for free, forever.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/seed-round-bring-node-red-to-enterprise/FlowFuse raises $7.25M Seed Round to bring Node-RED to the EnterpriseAllowing all developers to integrate IT and OT through low-code2022-11-03T17:00:00ZZJ van de Weg<p>Since the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/04/first-deploy/">launch of FlowFuse</a> in April 2021 our goal has been to
build a low-code, enterprise-ready development platform based on Node-RED. We
wanted to make it easier for enterprises to adopt, integrate, and scale Node-RED
into their existing environments. We published our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/">first version</a>
at the start of this year and have continued the journey to the
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/">launch of FlowFuse v1.0</a> just last week. Each of these releases
has moved us towards our goal of creating a platform for integrating the
IT and OT for all organizations, built around an open core.</p>
<!--more-->
<h3 id="flowfuse-is-node-red-for-the-enterprise" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/seed-round-bring-node-red-to-enterprise/#flowfuse-is-node-red-for-the-enterprise"></a> FlowFuse is Node-RED for the enterprise</h3>
<p>Node-RED is an incredible tool that has allowed many organizations to quickly
and easily integrate both their IT and OT. The sheer range and variety of
solutions created with Node-RED today go far beyond what we imagined. But we’ve
also seen the challenges faced by companies wanting to take their Node-RED
solutions into production and beyond.</p>
<p>This is where FlowFuse comes in. It addresses those challenges by adding
security, collaboration and deployment capabilities.</p>
<h3 id="our-%247.25m-seed-round" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/seed-round-bring-node-red-to-enterprise/#our-%247.25m-seed-round"></a> Our $7.25M Seed Round</h3>
<p>Today, we’re super excited to announce a $7.25M seed round led by Cota Capital,
joined by Westwave Capital, Uncorrelated Ventures, and Open Core Ventures. This
brings a huge amount of extensive knowledge and experience in IoT, open source,
enterprise-ready software solutions.</p>
<p>This investment will enable us to continue growing the team and platform to
realize our vision of FlowFuse being the best way to achieve enterprise-ready
Node-RED. It will enable us to invest back into the core Node-RED project to
further its development. One of our core company principles is around Open
Source Stewardship - the success of FlowFuse relies on a strong and successful
Node-RED community.</p>
<p>We’ll be bringing more collaboration features to the FlowFuse platform to
enable true collaborative, team-based working. We’ll expand our
<a href="https://flowfuse.com/docs/user/concepts/#device">Devices offering</a> to make it
far easier to run Node-RED wherever it suits your use-case, from data center to
remote edge locations.</p>
<p>Above all, we’ll be building a company that is open, sustainable, and fun to
work at. We already have some job openings available - check out our
<a href="https://boards.greenhouse.io/flowfuse">jobs board</a> if you’re interested in joining our
team.</p>
<p>If you’re interested in learning more about what we’re doing, or have any
questions, please do <a href="https://flowfuse.com/contact-us/">get in touch</a>!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/FlowFuse 1.0 releasedFlowFuse at 1.0, a huge milestone for us.2022-10-27T18:00:00ZRob Marcer<p>Predefined environment variables for your Instances and Devices, manage your Project's modules and import your existing flows (and credentials) into your FlowFuse <a href="https://flowfuse.com/docs/user/concepts/#instance">Projects</a>.</p>
<!--more-->
<!-- Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above. -->
<p>We're pleased to announce version 1.0 FlowFuse is now available. Keep reading for a promotion code to get your first month free on FlowFuse. Version 1.0 represents our vision of the base set of features needed for you to get great value from using FlowFuse in a production environment. That's not to say we are done, we will continue to add features, improve our interfaces and fix bugs with the same enthusiasm as we've worked towards 1.0. We'd like to hear your feedback on what we will be including in <a href="https://github.com/orgs/FlowFuse/projects/5">1.1 and beyond</a>.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/841">Standard Environment Variables set for both Projects and Devices</a></p>
<p>Projects now get a set of predefined environment variables that can be used by their flows. These give your flows access to the projects unique id and name. When the flows are deployed to <a href="https://flowfuse.com/docs/user/concepts/#device">devices</a>, they also get the device's id and name. That makes is easier to deploy flows across multiple devices and have each able to identify itself.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/405">Add additional node modules to your projects</a>
This feature allows you to pre-define additional Node-RED nodes and node modules you may want to be installed in your FlowFuse project, making it easier to manage.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/835">Import existing projects into FlowFuse</a></p>
<p>You can now import your existing flow and credentials files straight into your FlowFuse project - making it really easy to move your existing projects into the platform.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#improvements"></a> Improvements</h2>
<p>We've made a number of improvements to the overall experience of running FlowFuse.</p>
<ul>
<li>Editable Stack labels <a href="https://github.com/FlowFuse/flowfuse/issues/915">#915</a></li>
<li>Check for suitable version of Node on Devices <a href="https://github.com/FlowFuse/device-agent/issues/37">#37</a></li>
<li>Realtime Project status details in Project overview <a href="https://github.com/FlowFuse/flowfuse/issues/990">#990</a></li>
<li>Improve Template creation & Edit Project Settings UX <a href="https://github.com/FlowFuse/flowfuse/issues/1041">#1041</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1143">Pressing return in search box reloads page</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1126">Vue Router Warn</a></li>
<li><a href="https://github.com/FlowFuse/forge-ui-components/issues/58">Kebab menu in Settings breaks</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1096">flowforge-nr-launcher missing try/catch on http request</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1145">Invite with + in email address is incorrectly sanitised</a></li>
<li><a href="https://github.com/FlowFuse/forge-ui-components/issues/59">Table does not sort correctly when empty fields are present</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/929">4xx Errors not shown in App</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1076">Inconsistent errors returned from the API</a></li>
<li><a href="https://github.com/FlowFuse/flowforge-nr-launcher/issues/77">Module install not working on windows</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1038">Avatar lettering is mis-allinged when only rendering 1 character</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/968">it.only is not prohibited</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/966">No feedback when an API error occurs editing user</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1040">Start action is available on a running project</a></li>
<li></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<p><a href="https://github.com/Jozefik">Jozefik</a> for their work on <a href="https://github.com/FlowFuse/flowfuse/pull/1082">Adding limits to admin panel</a>.</p>
<p>As an open-source project, we welcome community involvement in what we're building. If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#try-it-out"></a> Try it out</h3>
<p><a href="https://app.flowfuse.com/account/create?code=RELEASE1">Sign up for FlowFuse-Managed Premium</a> with this link or at the checkout enter the code <strong>RELEASE1</strong> to get your first project free for a month. As an open source project you can also use <a href="https://flowfuse.com/docs/install/">FlowFuse-Community</a> for free, forever.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>Our managed <a href="https://app.flowfuse.com/">FlowFuse</a> is already running 1.0. Upgrade your project Stacks to the latest version to make sure you get all the latest changes.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/flowforge-1-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Customers of FlowFuse Cloud can raise a ticket by emailing support@flowfuse.com</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/db-migration-01/Scheduled maintenance: Database encryption October 2022Moving to encrypted PostgreSQL storage2022-10-18T00:00:00ZBen Hardill<p>As part of an on-going security <a href="https://flowfuse.com/product/security/#data-at-rest">review</a> of the FlowFuse Cloud offering we discovered that the backend database was not using encrypted storage. In keeping with industry best practices we plan to migrate the database to a new instance using encrypted at rest storage.</p>
<!--more-->
<h2 id="impact" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/db-migration-01/#impact"></a> Impact</h2>
<p>Customers' Node-RED instances will remain running, though any features that depend on FlowFuse will not operate as expected during the migration. This includes user sessions, project and team management, as well as the project nodes for inter-project communication.</p>
<p>Self-hosted installations are unaffected by this change.</p>
<h2 id="when" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/db-migration-01/#when"></a> When</h2>
<p>The migration will be on 26 October 2022 at 22:00 UTC and is expected to take under 2 hours. The platform will be available as soon as the migration is complete.</p>
<p>We will post updates during the migration period to our Twitter account <a href="https://twitter.com/flowforgeinc">@FlowFuseInc</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/Install FlowFuse Docker on Google CloudStep by step instructions to get FlowFuse Docker running on Google Cloud2022-10-14T00:00:00ZRob Marcer<p>As part of our preparations for FlowFuse 1.0 we have been testing various real world scenarios to see where we can add to our documentation and where we might be able to improve our releases to make the install process easier for users. As a benefit of that testing we have been able to hone these installation processes and we wanted to share one of those with you today.</p>
<!--more-->
<p>In this first of three articles, we are going to run through the process for installing FlowFuse on Google Cloud Platform (GCP) within a virtual machine (VM) using Docker.</p>
<p>We have set ourselves the goal of delivering a production environment. We want this installation benefit from:</p>
<ul>
<li>Email alerts (emails to users when they are added to teams etc)</li>
<li>HTTPS access to the install</li>
<li>FlowFuse <a href="https://flowfuse.com/docs/user/concepts/#device">Device</a> deployment via the included MQTT server that comes in our Docker build</li>
</ul>
<p>We will follow up with a second article covering the process of getting HTTPS running then we will close out the series by covering how to use key features of FlowFuse including <a href="https://flowfuse.com/docs/user/concepts/#device">Devices</a>.</p>
<h1 id="prerequisites" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#prerequisites"></a> Prerequisites</h1>
<ul>
<li>A domain name - We've registered flowforge-demo.com to demonstrate these steps</li>
<li>A DNS provider - Our Domain registrar provides a basic DNS service for free</li>
<li>A GCP account - Google will often give you free service credits on sign up so setting up FlowFuse on GCP should not cost you anything for at least a few weeks</li>
<li>An email provider which will allow SMTP connections to send email - To manage users on your FlowFuse platform you will need to be able to send emails to them. We have used a Google Workspace account for this purpose</li>
</ul>
<h1 id="gcp-vm-creation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#gcp-vm-creation"></a> GCP VM Creation</h1>
<p>Create a GCP account, once logged in navigate to Compute Engine then VM Instances. Select Create Instance you should now be <a href="https://console.cloud.google.com/compute/instancesAdd?project">here</a>.</p>
<p>Give your instance a name, select a Region and Zone. I have found that the default machine configuration works fine but depending on your project you may wish to change the resources.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/1-2iwAT9cmjZ-565.avif 565w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/1-2iwAT9cmjZ-565.webp 565w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the interface for creating GCP VM" alt=""Screenshot showing the interface for creating GCP VM"" loading="lazy" decoding="async" src="https://flowfuse.com/img/1-2iwAT9cmjZ-565.jpeg" width="565" height="607" /></picture></p>
<p>You now need to allow access to your FlowFuse installation from the internet. In the Firewall section tick Allow HTTP traffic and Allow HTTPS traffic.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/2-NnppdgkreY-537.avif 537w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/2-NnppdgkreY-537.webp 537w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the firewall section in the interface for creating a GCP VM" alt=""Screenshot showing the firewall section in the interface for creating a GCP VM"" loading="lazy" decoding="async" src="https://flowfuse.com/img/2-NnppdgkreY-537.jpeg" width="537" height="122" /></picture></p>
<p>Next up, assign a static IP address to the VM. Click Advanced options, then Networking. Now scroll down until you see Network interfaces and click on default to expand that section. In External IPv4 address select Create IP Address, give it a name than press Reserve.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/3-vqBZBbWboS-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/3-vqBZBbWboS-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the network section in the interface for creating a GCP VM" alt=""Screenshot showing the network section in the interface for creating a GCP VM"" loading="lazy" decoding="async" src="https://flowfuse.com/img/3-vqBZBbWboS-650.jpeg" width="650" height="846" /></picture></p>
<p>Once you have reserved your IP it will be shown in the External IPv4 address field, write it down as we will need it later to create the DNS records. Our IP address was 34.125.156.130.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/4-5_Hcq0Ziym-495.avif 495w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/4-5_Hcq0Ziym-495.webp 495w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing your reserved IP in the External IPv4 address field" alt=""Screenshot showing your reserved IP in the External IPv4 address field"" loading="lazy" decoding="async" src="https://flowfuse.com/img/4-5_Hcq0Ziym-495.jpeg" width="495" height="59" /></picture></p>
<p>You are now ready to create and boot your VM, scroll to the bottom of the page and press Create. It can take a minute or two for the VM to be ready to use.</p>
<h1 id="dns-set-up" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#dns-set-up"></a> DNS Set Up</h1>
<p>So that you can run FlowFuse on your newly created GCP VM you will need to set up 2 DNS records. These records are slightly different to what is suggested in the FlowFuse install docs. We were keen to be able to run other services on this domain so we set up the following records.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/5-U70Zi0GX3B-644.avif 644w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/5-U70Zi0GX3B-644.webp 644w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing interface for setting DNS" alt="Screenshot showing interface for setting DNS" loading="lazy" decoding="async" src="https://flowfuse.com/img/5-U70Zi0GX3B-644.jpeg" width="644" height="283" /></picture></p>
<p>DNS changes need to propagate, and depending on your DNS provider, ISP, and other factors, this can take anywhere between a few seconds to 4 hours. Our’s were in place very quickly. To validate the DNS records you can use <code>dig</code> on either a Mac or Linux.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/6-FjA4QESL4_-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/6-FjA4QESL4_-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing output of the dig command" alt="Screenshot showing output of the dig command" loading="lazy" decoding="async" src="https://flowfuse.com/img/6-FjA4QESL4_-650.jpeg" width="650" height="615" /></picture></p>
<p>The DNS records are set to the IP record we noted down earlier, so we're good to continue.</p>
<h1 id="flowfuse-docker-installation" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#flowfuse-docker-installation"></a> FlowFuse Docker Installation</h1>
<p>The next step is to install Docker on our GCP VM. If you return to GCP you should see that your VM is now up and running, you can now click on SSH to connect to your VM. This will open up a browser based SSH session to your VM.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/7-mYM--JfUvg-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/7-mYM--JfUvg-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing access to SSH in GCP" alt="Screenshot showing access to SSH in GCP" loading="lazy" decoding="async" src="https://flowfuse.com/img/7-mYM--JfUvg-650.jpeg" width="650" height="124" /></picture></p>
<p>Once you have a Secure Shell (SSH) session open, the first step is to install Docker using the following commands.</p>
<p><code>sudo apt-get update</code></p>
<pre><code>sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
</code></pre>
<p><code>sudo mkdir -p /etc/apt/keyrings</code></p>
<p><code>curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg</code></p>
<pre><code>echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
</code></pre>
<p><code>sudo apt-get update</code></p>
<p><code>sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin</code></p>
<p>You can read a lot more detail about what each these commands actually do <a href="https://docs.docker.com/engine/install/debian/">here</a>.</p>
<h1 id="download-flowfuse%E2%80%99s-latest-docker-build" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#download-flowfuse%E2%80%99s-latest-docker-build"></a> Download FlowFuse’s latest Docker build</h1>
<p>The next step is to get the codebase for FlowFuse onto your VM, to do so you will need to run the following commands. Please note that we are working with our 0.10.0 build, you will need to update the version number in the commands below if you are working with a newer build.</p>
<p>Use curl to download the files we need.</p>
<p><code>sudo curl -L https://github.com/FlowFuse/docker-compose/archive/refs/tags/v0.10.1.tar.gz -o v0.10.1.tar.gz</code></p>
<p>Make the directory where we will store FlowFuse.</p>
<p><code>sudo mkdir /opt/flowforge</code></p>
<p>Uncompress FlowFuse and save it to the directory.</p>
<p><code>sudo tar zxf v0.10.1.tar.gz --directory /opt/flowforge</code></p>
<p>You should now have all the code you need for FlowFuse in the directory <code>/opt/flowforge/docker-compose-0.10.1</code>, it should look something like this.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/8-VQIuM51uhR-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/8-VQIuM51uhR-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing directory listing for FlowFuse" alt=""Screenshot showing directory listing for FlowFuse"" loading="lazy" decoding="async" src="https://flowfuse.com/img/8-VQIuM51uhR-650.jpeg" width="650" height="269" /></picture></p>
<h1 id="configure-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#configure-flowfuse"></a> Configure FlowFuse</h1>
<p>We can now configure FlowFuse on your VM. We are going to need to edit two files. Firstly we need to switch into the directory where we just placed FlowFuse.</p>
<p><code>cd /opt/flowforge/docker-compose-0.10.1</code></p>
<p>Then we need to edit the flowforge.yml file, we're using Nano to do that.</p>
<p><code>sudo nano /opt/flowforge/docker-compose-0.10.1/etc/flowforge.yml</code></p>
<p>At the top of the file you need to update the domain and base_url to match your domain</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/9-fq6g179_er-494.avif 494w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/9-fq6g179_er-494.webp 494w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing domain configuration in flowforge.yml" alt=""Screenshot showing domain configuration in flowforge.yml"" loading="lazy" decoding="async" src="https://flowfuse.com/img/9-fq6g179_er-494.jpeg" width="494" height="163" /></picture></p>
<p>Next we will need to edit the Email Configuration section to match your SMTP provider. Set enabled to true then add in the details provider by your email provider. For example in this case I am using our Google Workspace account.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/10-z1cSwwzHXQ-405.avif 405w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/10-z1cSwwzHXQ-405.webp 405w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing email configuration in flowforge.yml" alt=""Screenshot showing email configuration in flowforge.yml"" loading="lazy" decoding="async" src="https://flowfuse.com/img/10-z1cSwwzHXQ-405.jpeg" width="405" height="215" /></picture></p>
<p>Finally, you need to update the <code>public_url</code> for your mqtt broker to match your DNS record.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/11-7CI9XBq2PI-370.avif 370w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/11-7CI9XBq2PI-370.webp 370w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing MQTT configuration in flowforge.yml" alt=""Screenshot showing MQTT configuration in flowforge.yml"" loading="lazy" decoding="async" src="https://flowfuse.com/img/11-7CI9XBq2PI-370.jpeg" width="370" height="55" /></picture></p>
<p>You can now save and close that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key.</p>
<p>Now we need to edit the <code>docker-compose.yml</code> file. We will use Nano again to do that.</p>
<p><code>sudo nano /opt/flowforge/docker-compose-0.10.1/docker-compose.yml</code></p>
<p>We need to edit the file to add in to the domain as follows.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/12-xO2b_hIwE5-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/12-xO2b_hIwE5-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing virtual hosts configuration in docker-compose.yml" alt=""Screenshot showing virtual hosts configuration in docker-compose.yml"" loading="lazy" decoding="async" src="https://flowfuse.com/img/12-xO2b_hIwE5-650.jpeg" width="650" height="748" /></picture></p>
<p>Save and exit from that file, in Nano you can do that by pressing ‘control x’ then ‘y’ then the Return key.</p>
<h1 id="start-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/ff-docker-gcp/#start-flowfuse"></a> Start FlowFuse</h1>
<p>We are now ready to start up FlowFuse for the first time, to do so we will use the following command.</p>
<p><code>sudo docker compose -p flowforge up -d</code></p>
<p>The build process will take a few minutes, once it’s completed let’s make sure all the docker containers are running.</p>
<p><code>sudo docker ps</code></p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/13-O_BeRnUDb_-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/13-O_BeRnUDb_-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="Docker PS output" loading="lazy" decoding="async" src="https://flowfuse.com/img/13-O_BeRnUDb_-650.jpeg" width="650" height="75" /></picture></p>
<p>You should see 4 running Docker containers.</p>
<p>If everything went well you should now be able to access your FlowFuse server via the DNS record you created.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/14-0HT-Ly1_ym-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/14-0HT-Ly1_ym-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="FF Login page" loading="lazy" decoding="async" src="https://flowfuse.com/img/14-0HT-Ly1_ym-650.jpeg" width="650" height="721" /></picture></p>
<p>Nice, you now have a working instance of FlowFuse running on GCP but remember that all traffic is currently running on HTTP so we still have some work to do.</p>
<p>In the next article we will cover how to add HTTPS support to this FlowFuse installation.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/10/community-news-09/Community News September 2022News from the FlowFuse and Node-RED communities2022-10-07T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter, a regular roundup of what’s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/"><strong>FlowFuse 0.10</strong></a>
Version 0.10 was released on 30th September. Our latest release includes some great new features, quality of life improvements and bug fixes. Notable additions included the ability to <a href="https://github.com/FlowFuse/flowfuse/pull/893">Secure your HTTP endpoints</a>, <a href="https://github.com/FlowFuse/flowfuse/issues/657">Add read-only users to your projects</a> and <a href="https://flowfuse.com/docs/cloud/introduction/#ip-addresses">use our static IP address for outbound connections</a></p>
<p>If you’d like to learn more about what else was included in 0.10 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/">blog post</a>, on our <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v0.10.0">GitHub release page</a> and on our <a href="https://youtube.com/watch?v=mjR1iiEFiBg">Youtube channel</a>. We’d also love for more of you to get involved in the development of FlowFuse, <a href="https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md">contributions to the code</a> and <a href="https://github.com/FlowFuse/flowfuse/issues">bug reports</a> are really appreciated.</p>
<p><a href="https://nrcon.nodered.org/"><strong>Node-RED Con 2022</strong></a><br />
We are happy to again be involved in Node-RED con. The event is being held online on 7th October, with content for both English and Japanese speakers including our colleagues Nick O'Leary and Sam Machin. You can find out more on the <a href="https://nrcon.nodered.org/">Node-RED Con website</a>.</p>
<p><a href="https://flowforge.com/team/"><strong>FlowFuse Team News</strong></a><br />
We are currently recruiting <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004">NodeJS Developers</a>, if you’re interested in joining our team please <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004#app">apply here</a>.</p>
<p>We are also looking for a <a href="https://boards.greenhouse.io/flowfuse/jobs/4687876004">PeopleOps Manager</a> to help us grow our team. You can <a href="https://boards.greenhouse.io/flowfuse/jobs/4687876004#app">apply here</a> for that position.</p>
<p><a href="https://hackster.io/user102774/fight-fire-wild-fire-prediction-using-tinyml-df7572"><strong>Forest Fire Alerts Using ML, IOT and Node-RED</strong></a><br />
This fascinating project came up a few days ago and we wanted to share it with you all. The concept is to use a mesh network of IoT devices to monitor various indicators of potential and current wildfires and report data back to the relevant authorities. The system will use ML to predict wildfire risk levels and hopefully send warnings before a fire actually starts. The two developers <a href="https://linkedin.com/in/zainmfd/">Muhammed</a> and <a href="https://linkedin.com/in/salmanfarisvp/">Salman</a> are planning to use Node-RED to manage the reporting of fires to the authorities.</p>
<p><a href="https://app.flowfuse.com/account/create"><strong>Try FlowFuse for Free</strong></a><br />
As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on FlowFuse when you create a new team. To get this discount please follow <a href="https://app.flowfuse.com/account/create?code=RELEASE010">this link</a> or use the code RELEASE010 when on the payment page after creating a new team.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/FlowFuse 0.10 releasedSecure HTTP end points, Read-only users and Static outbound IPs2022-09-30T12:00:00ZRob Marcer<p>Secure your HTTP endpoints, create read-only users in your teams and use our static IP address for outbound traffic</p>
<!--more-->
<p>Keep reading for the details of what's in this release or you can watch our 1 minute roundup video of the new release above.</p>
<p>We're pleased to announce version 0.10 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes. Keep reading for a promotion code to get your first month free on FlowFuse Cloud.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/578">Secure HTTP Endpoints</a>
We've added the ability for you to secure your HTTP endpoints. You can now control who can access Dashboards or API endpoints you create in FlowFuse.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/657">Read-only Users</a>
We've added a new user role for Read-only access. This will allow users to login to your FlowFuse project and view the Node-RED flows without them being able to edit anything.</p>
<p><a href="https://flowfuse.com/docs/cloud/introduction/#ip-addresses">Static Outbound IP Addresses</a>
We've updated FlowFuse Cloud so that all outbound traffic from your projects now comes from a single IP address. When trying to access a remote resource such as a database it is often a requirement for the IP address the traffic comes from to be fixed.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#improvements"></a> Improvements</h2>
<p>We've made a number of improvements to the overall experience of running FlowFuse.</p>
<ul>
<li>Allow both key and component in a ff-data-table column definition <a href="https://github.com/FlowFuse/forge-ui-components/issues/43">#43</a></li>
<li>Default Stack and Templates <a href="https://github.com/FlowFuse/flowfuse/issues/989">#989</a></li>
<li>Provide platform containers and base stack container for administrators <a href="https://github.com/FlowFuse/flowfuse/issues/917">#917</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/917">Provide platform containers and base stack container for administrators</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/983">User names can be same (but different case)</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/463">User list not refreshing after changing user details</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/986">Navigating directly to a device page gets the wrong team selected</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/941">Node-RED Isn't ready when FlowFuse app says it is running following a project restart</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/923">Invitations left for deleted teams</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/1024">Following email verification link twice throws error</a></li>
<li><a href="https://github.com/FlowFuse/device-agent/issues/21">Agent does not log stderr from the Node-RED process</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/948">On Kubernetes project names can not start with a number</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/930">When creating projects stack options do not wrap</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/979">Save button in admin user-edit dialog doesn't close dialog</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/911">Setting UI doesn't allow me to update settings</a></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<p><a href="https://github.com/Pezmc">Pezmc</a> for their work on <a href="https://github.com/FlowFuse/flowfuse/pull/949">Add device count and project counts by type to admin</a></p>
<p><a href="https://github.com/ArshErgon">ArshErgon</a> for their work on <a href="https://github.com/FlowFuse/flowfuse/pull/977">Update vue component name for NoVerifiedEmail.vue</a></p>
<p>As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#try-it-out"></a> Try it out</h3>
<p><a href="https://app.flowfuse.com/account/create?code=RELEASE010">Sign up for FlowFuse Cloud</a> with this link or at the checkout enter the code <strong>RELEASE010</strong> to get your first project free for a month.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 0.10 and the stacks updated. Upgrade your project stacks to the latest version to make sure you get all the latest changes.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-010-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Customers of FlowFuse Cloud can raise a ticket by emailing support@flowfuse.com</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/static-ips/Static Outbound IP AddressesStatic IP addresses are here for your FlowFuse Cloud projects’ outbound connections2022-09-27T00:00:00ZRob Marcer<p>On Friday last week we updated FlowFuse Cloud to use a static IP address for outbound traffic. This will allow you to predict which IP address your traffic will come from for example when traversing a firewall or accessing a remote database.</p>
<!--more-->
<p>You will need to manually suspend then start each of your projects (a restart will not move your projects to the fixed IP address). Once that action is completed all outbound connections will come from one of our static IP address.</p>
<p>Any inbound traffic should still use the hostname assigned to each of your projects, you cannot use our IP address to route http traffic to your projects.</p>
<p>You can view our IP address in the <a href="https://flowfuse.com/docs/cloud/introduction/#ip-addresses">Docs</a> section of our website.</p>
<p>If you’d like to stay up to date with our latest releases you can do so on <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/">our blog</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/community-news-08/Community News August 2022News from the FlowFuse and Node-RED communities2022-09-16T00:00:00ZRob Marcer<p>Welcome to the FlowFuse newsletter, a regular roundup of what’s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/"><strong>FlowFuse 0.9</strong></a><br />
Version 0.9 was released on 1st September. Our latest release includes some great new features, quality of life improvements and bug fixes. Notable additions included the ability to <a href="https://github.com/FlowFuse/flowfuse/pull/893">suspend your projects</a>, <a href="https://github.com/FlowFuse/flowfuse/pull/856">login with your email</a> and <a href="https://github.com/FlowFuse/flowfuse/issues/774">define custom paths for your dashboards</a>.</p>
<p>If you’d like to learn more about what else was included in 0.9 you can do so on our <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/">blog post</a>, on our <a href="https://github.com/FlowFuse/flowfuse/releases/tag/v0.9.0">GitHub release page</a> and on our <a href="https://www.youtube.com/watch?v=d23Pmyc0k7I">Youtube channel</a>. We’d also love for more of you to get involved in the development of FlowFuse, <a href="https://github.com/FlowFuse/flowfuse/blob/main/CONTRIBUTING.md">contributions to the code</a> and <a href="https://github.com/FlowFuse/flowfuse/issues">bug reports</a> are really appreciated.</p>
<p><a href="https://nrcon.nodered.org/"><strong>Node-RED Con 2022</strong></a><br />
We are happy to again be involved in Node-RED con. The event is being held online on 7th October, with content for both English and Japanese speakers. You can find out more on the <a href="https://nrcon.nodered.org/">Node-RED Con website</a>.</p>
<p><a href="https://flowfuse.com/team/"><strong>FlowFuse Team News</strong></a><br />
We’d like to welcome Rob Marcer to the FlowFuse team. Rob has joined as our Developer Educator, he's going to work to help you get the best value from FlowFuse by developing our documentation and community support.</p>
<p>We are also recruiting for <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004">NodeJS Developers</a>, if you’re interested in joining our team please <a href="https://boards.greenhouse.io/flowfuse/jobs/4463977004#app">apply here</a>.</p>
<p><a href="https://twitter.com/Docker/status/1559919666721693699?t=QBzGGzY2kJ12Z5aoi1QPTA"><strong>Official Node-RED Docker Image Passes Milestone</strong></a><br />
Docker has <a href="https://twitter.com/Docker/status/1559919666721693699?t=QBzGGzY2kJ12Z5aoi1QPTA">announced</a> that the Node-RED Docker image has now been downloaded over 100 million times. They have also created a <a href="https://www.docker.com/blog/build-retail-store-items-detection-system-no-code-ai/?utm_campaign=2022-08-17-brnd-nocode&utm_medium=social&utm_source=twitter">guide to using Node-RED</a> to Build and Deploy a Retail Store Items Detection System Using No-Code AI Vision at the Edge. We think it’s worth a read.</p>
<p><a href="https://wokwi.com/"><strong>Simulating IOT Projects in Your Browser</strong></a><br />
While not directly related to FlowFuse we’ve enjoyed wasting a little too much time looking at the simulated IOT projects on <a href="https://wokwi.com/">Wokwi</a>. The <a href="https://wokwi.com/projects/328451800839488084">Simon Game with Score</a> project is a little too addictive.</p>
<p><a href="https://app.flowfuse.com/account/create"><strong>Try FlowFuse for Free</strong></a><br />
As a thank you for reading our newsletters we’d like to offer you a free, small project for one month on FlowFuse when you create a new team. To get this discount please use the code RELEASE09 when on the payment page after creating a new team.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/FlowFuse 0.9 releasedSuspended projects, login with email and Team Types2022-09-01T12:00:00Z<p>Suspend your projects when you don't need them, login with either your username or email, and introducing Team Types</p>
<!--more-->
<p>Keep reading for the details of what's in this release our you can watch our 1 minute roundup video of the new release above.</p>
<p>We're pleased to announce version 0.9 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes. Keep reading for a promotion code to get your first month free on FlowFuse Cloud.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/893">Suspend Projects</a>
Sometimes you want to put a project to one side for a while, maybe your development has stalled or you're waiting on something external to be ready. Perhaps you don't need it to be running all the time. With the 0.9 release we've added the ability to suspend a project. Once suspended, your flows are safely stored in the platform database, but Node-RED isn't running and the project doesn't consume any resources. In FlowFuse Cloud we do not charge you for suspended projects - you only pay when the project is running.
Your project will be there ready to start back up when you need it with just one click.
Remember that any context data or anything written to the filesystem will not persist through a restart or a suspend of a project.</p>
<p>Alongside this change, we've removed the option to 'stop' a project. That option would only stop Node-RED, but the underlying container would still be running, consuming resources. With Node-RED 3.0 adding the ability to stop the flows, but still be able to edit them, that provides a much better user experience.
You can still restart the Node-RED process from the Forge app as before for example when you have updated a package in your flows.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/733">Team Types</a>
We've introduced another concept into the platform with this release. Team Types will allow us to offer more advanced features to teams on FlowFuse Cloud. You won't see much difference in this release but it allows us to build on in future releases.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/695">PostHog Analytics</a>
We've changed the analytics tooling integrated into the platform. With this release, we've deprecated the use of Plausible Analytics as it didn't quite provide the sort of insight we wanted. We now integrate with <a href="https://posthog.com/">PostHog</a>. They share our ethos and approach to open source and self hosting - something you can take advantage of if you're running your own FlowFuse platform.
For FlowFuse Cloud, the data is sent to our PostHog account so we can better understand how the platform is being used. If you're running your own instance, the information is only captured if you configure it with your own PostHog instance details - it does not send any data back to us.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/856">Login with email</a>
A common problem that we've seen from users is trying to login with their email address instead of their username. As of 0.9 you can now enter either at the login screen.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/774">Custom Dashboard Path</a>
If you are using the Node-RED Dashboard set of nodes, you can now change the path where the dashboard will be served from. The default is still <code>/ui</code> but you can now move that onto <code>/</code> or anything else. This is helpful when migrating existing projects over to FlowFuse.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#improvements"></a> Improvements</h2>
<p>We've made a number of improvements to the overall experience of running FlowFuse.</p>
<ul>
<li>Improvements to the FlowFuse Theme <a href="https://github.com/FlowFuse/flowfuse/pull/883">#883</a>.</li>
<li>Upper-case characters in Project Names <a href="https://github.com/FlowFuse/flowfuse/issues/546">#546</a></li>
<li>Password reset requests are logged<a href="https://github.com/FlowFuse/flowfuse/issues/773">#773</a></li>
<li>Admin can manually verify users email <a href="https://github.com/FlowFuse/flowfuse/issues/692">#902</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/875">Cannot edit template settings</a><br /></li>
<li><a href="https://github.com/FlowFuse/nr-project-nodes/issues/10">Project Link Nodes Appear in CE Install</a></li>
<li><a href="https://github.com/FlowFuse/nr-project-nodes/issues/14">Project Link Nodes MQTT Connection</a></li>
<li><a href="https://github.com/FlowFuse/flowforge-nr-theme/issues/19">Theme shows white characters on white background</a></li>
<li><a href="https://github.com/FlowFuse/device-agent/issues/27">Changing Project on device doesn't remove old modules</a></li>
<li><a href="https://github.com/FlowFuse/device-agent/issues/30">Device Agent and Node-RED use different time in logs</a></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<p><a href="https://github.com/bonanitech">Bonantech</a> for his work <a href="https://github.com/FlowFuse/flowforge-nr-theme/commit/30e21a3777dc3438ef206157ee9110728011f59c">cleaning up the theme CSS</a></p>
<p>As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our <a href="https://flowforge.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="try-it-out" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#try-it-out"></a> Try it out</h3>
<p><a href="https://app.flowfuse.com/account/create">Sign up for FlowFuse Cloud</a> and at the checkout enter the code <strong>RELEASE09</strong> to get your first project free for a month.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 0.9 and the stacks updated. Upgrade your project stacks to the latest version and start using the Project Link nodes now.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/09/flowforge-09-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Customers of FlowFuse Cloud can raise a ticket by emailing support@flowfuse.com</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/community-news-06/Community News July 2022News from the FlowFuse and Node-RED communities2022-08-11T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/">FlowFuse 0.8 </a><br />
Version 0.8 was released, notable features include the new Project Link nodes for sharing data between projects, and the ability to stop and start flows within Node-RED.
We've updated the format of our release posts as well to detail all the user facing changes, from new Features through to small improvements and bugs.</p>
<p><a href="https://www.tomshardware.com/how-to/raspberry-pi-pico-w-node-red">Raspberry Pi Pico with Node-RED</a><br />
<a href="https://twitter.com/biglesp">Les Pounder</a> Has published a great tutorial on communicating with the new <a href="https://www.raspberrypi.com/products/raspberry-pi-pico/">Raspberry Pi Pico W</a> and Node-RED. He shows you how to capture data from an environmental sensor then display this on a dashboard.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/new-projecttype/">Medium Projects</a><br />
We've added a second size of project to FlowFuse Cloud. A bigger project type with more resources available to it.</p>
<p><a href="https://blog.golioth.io/how-to-use-node-red-to-control-iot-devices-on-golioth/">Using Node-RED to control IoT Devices on Golioth</a><br />
Our friends at <a href="https://golioth.io/">Golioth</a> have published an article on how to use Node-RED to control and process data from IoT Devices connected to their platform.</p>
<p><a href="https://discourse.nodered.org/t/node-red-2-2-3-and-3-0-2-released/66018">Node-RED 3.0.2 and 2.2.3</a><br />
Node-RED 3.0.2 has been released fixing some bugs in the 3.0.0 release, The Node-RED 2.x stream has also had a maintenance release with many of the fixes in 3.0 back-ported. As usual these are already available as stacks on FlowFuse Cloud.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/FlowFuse 0.8 releasedInter-Project Communication, Default Teams, and realtime device management.2022-08-04T12:00:00Z<p>Easily pass messages between your projects on the cloud or devices, UX improvements, and more.</p>
<!--more-->
<p>Keep reading for the details of whats in this release our you can watch our 1 minute roundup video of the new release above.</p>
<p>We're pleased to announce version 0.8 is now available. The next release of the FlowFuse application containing new features, a number of improvements, and bug fixes.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/662">Project Link Nodes</a>
We've introduced our first custom FlowFuse nodes to the palette of new projects. The Project Link nodes allow you to easily pass data between different projects within the same team.
These projects can be running in the cloud or on devices, with the communication powered by our own internal MQTT broker.
Try these out today on FlowFuse Cloud by creating a new project or updating your existing project's stack. There's more information in the <a href="https://github.com/FlowFuse/nr-project-nodes/blob/main/README.md">README</a> for the nodes.
For local installs of FlowFuse, the nodes are only available with an Enterprise Edition license.
<picture><source type="image/webp" srcset="https://flowfuse.com/img/ProjectLink-3NO4LgATRJ-650.webp 650w, https://flowfuse.com/img/ProjectLink-3NO4LgATRJ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/gif" srcset="https://flowfuse.com/img/ProjectLink-3NO4LgATRJ-650.gif 650w, https://flowfuse.com/img/ProjectLink-3NO4LgATRJ-1300.gif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the message being sent from one project to another using project link nodes" alt=""Screenshot showing the message being sent from one project to another using project link nodes"" loading="lazy" decoding="async" src="https://flowfuse.com/img/ProjectLink-3NO4LgATRJ-650.webp" width="1300" height="472" /></picture></p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/839">Start & Stop Flows</a>
Node-RED 3.0 <a href="https://nodered.org/blog/2022/07/14/version-3-0-released#editing-stopped-flows">introduced a new feature</a> that allows you to stop your flows from processing requests while still being able to work in the editor and deploy changes. We've now enabled this feature within FlowFuse for projects running a Node-RED 3.x stack.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/298">Default Team</a>
If you are a member of multiple teams you can now set your preferred default saving you from having to change teams each time you log in.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#improvements"></a> Improvements</h2>
<p>We've made a number of improvements to the overall experience of running FlowFuse.</p>
<ul>
<li>Devices now communicate to the Forge application over MQTT instead of polling <a href="https://github.com/FlowFuse/flowfuse/issues/754">#754</a>. You'll need to update your Device Agent to the latest version to take advantage of this.</li>
<li>The table views have had a major overhaul allowing you to sort and search items <a href="https://github.com/FlowFuse/forge-ui-components/issues/28">#28</a></li>
<li>If the application receives an error you now see a notification in the UI. <a href="https://github.com/FlowFuse/flowfuse/issues/771">#771</a></li>
<li>The Verification email page has been cleaned up <a href="https://github.com/FlowFuse/flowfuse/issues/718">#718</a></li>
<li>The initial Thank-you page has been cleaned up <a href="https://github.com/FlowFuse/flowfuse/issues/648">#648</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/793">Logo Distorted in Safari</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/729">LocalFS Install doesn't check for Build Tools</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/pull/842">Users with Expired passwords can create teams</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/pull/790">Click-jacking Vulnerability</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/pull/824">Users with can create teams without verifying email</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowforge-nr-storage/issues/17">Occasional Timeout when deploying flows</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/833">Notification of member deletion contains internal ID</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/334">Pressing Enter in the Team Delete modal triggers cancel</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/751">Node-RED Isn't ready when Forge app says it is running (Docker)</a><br /></li>
</ul>
<h2 id="contributors" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#contributors"></a> Contributors</h2>
<p>We'd like the thank the following for their contributions to this release:</p>
<ul>
<li><a href="https://github.com/HaroldPetersInskipp">HaroldPetersInskipp</a> helped <a href="https://github.com/FlowFuse/flowfuse/pull/812">updating our documentation</a></li>
<li><a href="https://github.com/Steveorevo">Steveorevo</a> also <a href="https://github.com/FlowFuse/flowfuse/pull/818">updated our documentation</a></li>
</ul>
<p>As an open-source project, we welcome the community involvement in what we're building. If you're interested in contributing, checkout our <a href="https://flowfuse.com/docs/contribute/">guide in the docs</a>.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 0.8 and the stacks updated. Upgrade your project stacks to the latest version and start using the Project Link nodes now.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/08/flowforge-08-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Customers of FlowFuse Cloud can raise a ticket by emailing support@flowfuse.com</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/new-projecttype/Introducing Medium Projects on FlowFuse CloudA bigger project with more resources2022-07-22T12:00:00Z<p>We've added a second size of project to FlowFuse Cloud. A bigger project type with more resources available to it.</p>
<!--more-->
<p>Our <a href="https://flowforge.com/blog/2022/07/flowforge-07-released/">0.7 release</a> introduced the concept of Project Types. This allows platforms to provide different sizes of projects, varying the memory/cpu or features available within a given type.</p>
<p>Today we've put this feature to work on FlowFuse Cloud by introducing the new <strong>Medium Project</strong> type.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/project-type-IyfjtC-GCy-650.avif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/project-type-IyfjtC-GCy-650.webp 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the new stack selection feature" alt=""Screenshot showing the new stack selection feature"" loading="lazy" decoding="async" src="https://flowfuse.com/img/project-type-IyfjtC-GCy-650.jpeg" width="650" height="688" /></picture></p>
<p>Medium projects have 3 times the resources of the existing Small type, allowing for more complex flows and larger message objects. This will be useful to business users looking to process complex sets of data.</p>
<p>Our Medium project is priced at $50 a month and we'll be adding new features to this project type in the coming months to further enhance the value of this new tier.</p>
<p>We don't currently support directly upgrading a project between types, but that is in the <a href="https://github.com/FlowFuse/flowfuse/issues/595">plan for the future</a>. In the meantime, you can use the 'Export Project' feature on a project's settings tab to copy it over into a new Medium type project.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/community-news-06/Community News June 2022News from the FlowFuse and Node-RED communities2022-07-19T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/">FlowFuse 0.7 </a><br />
We've shipped the next version of FlowFuse, features in this release include the ability to Rollback a project to a previous snapshot, setting environment variables for a specific device and we've begun to unify the experience between the Forge app and the Node-RED editor with our own Node-RED theme.</p>
<p><a href="https://nodered.org/blog/2022/07/14/version-3-0-released">Node-RED 3.0 Released </a><br />
Node-RED 3.0 Has been officially released, there are a lot of improvements in the user experience of the editor, with new menus and junctions. There's also the ability to stop your flows while you can continue to edit and deploy. Take a look at the blog post for all the details. This is now the default stack on FlowFuse Cloud.</p>
<p><a href="https://discourse.nodered.org/t/node-red-flows-on-esp8266-and-esp32/64345">Node-RED for Microcontrollers</a><br />
<a href="https://twitter.com/phoddie">Peter Hoddie</a> of <a href="https://moddable.com/">Moddable</a> Has released some early <a href="https://github.com/phoddie/node-red-mcu">work</a> on getting the Node-RED runtime to execute a flow on a microcontroller like the ESP32. It's still very early days so don't expect to be building flows directly on the MCU, and the number of nodes that are supported is limited, but this is an interesting development for Node-RED in the IoT space.</p>
<p><a href="https://www.papercall.io/nrcon2022">Node-RED Con CFP</a><br />
A reminder about Node-RED Con and the call for papers, submissions close at the end of July so get your proposals in now.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/FlowFuse 0.7 releasedRollbacks, Device Environment Variables and a FlowFuse Theme2022-07-07T12:00:00Z<p>Rollback projects to a previous snapshot, improvements in using Devices, and more.</p>
<!--more-->
<p>Keep reading for the details of whats in this release our you can watch our 1 min roundup video of the new release above.</p>
<p>We're pleased to announce version 0.7 is now available. the next release of the FlowFuse application.</p>
<h2 id="features" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/#features"></a> Features</h2>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/587">Rollback</a>
FlowFuse is about running Node-RED at any scale, part of that scale is having multiple users collaborate on the same project. When you are collaborating with people it's important to be able to go back in time to a known working state. As part of that we are introducing rollbacks, this means that you can now take a snapshot of your project at a point in time and then make changes safe in the knowledge that you can rollback to that previous snapshot if you need to.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/680">Device Environment Variables</a>
In the last release we introduced the concept of devices. We're already learning from how these are used and one feature we've added in 0.7 is Device Environment Variables. You have been able to set Environment Variables at the project level but when deploying a snapshot to multiple devices you may want to override these values for each device, for example to set a site ID. With device specific variable users are enabled to differentiate based on the context in their flows.</p>
<p><a href="https://github.com/FlowFuse/flowforge-nr-theme/">FlowFuse Theme</a>
Now that we have a stronger visual identity in the Forge application we have continued that work through to the Node-RED editor. If you create or upgrade a project with a Node-RED 3.0 stack you will see a different theme in the editor. It's still very much Node-RED but just has some subtle hints to tie it back to the FlowFuse application. We will continue to iterate on this to further integrate the experience between FlowFuse and Node-RED in both directions.
<picture><source type="image/avif" srcset="https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-650.avif 650w, https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-650.webp 650w, https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-650.jpeg 650w, https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Screenshot showing the FlowFuse theme when the Node-RED 3.0 stack is selected" alt=""Screenshot showing the FlowFuse theme when the Node-RED 3.0 stack is selected"" loading="lazy" decoding="async" src="https://flowfuse.com/img/ff-07-theme-17e1ceCR8s-650.jpeg" width="1300" height="835" /></picture></p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/380">ProjectTypes</a>
The introduction of ProjectTypes is a way to group Stacks together that share common characteristics - such as memory/cpu limits, or the availability of particular features. In platforms with billing enabled, such as our own FlowFuse Cloud, the ProjectTypes can have different price points set on them. Within FlowFuse Cloud, you'll see we've introduced the Small ProjectType - which applies to all existing projects on the platform.</p>
<p><a href="https://github.com/FlowFuse/flowfuse/issues/694">Stack Versions</a>
This allows an admin to link different stacks together in their lineage. This allows administrators to nudge users to new Node-RED versions or upgrade pre-installed dependencies when running in a container environment. Any users with projects on an old version will be prompted that there is an update available, making it even easier to stay up to date with Node-RED versions when you build your flows on FlowFuse.</p>
<h2 id="improvements" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/#improvements"></a> Improvements</h2>
<p>We've made a number of improvements to the overall experience of running FlowFuse.</p>
<ul>
<li>The Team Switch menu has been moved to a more prominent position in the interface, this also makes it easier to see how to create a new team. <a href="https://github.com/FlowFuse/flowfuse/issues/616">#616</a></li>
<li>Notifications have had an overhaul, you will now see waiting invites on all pages. <a href="https://github.com/FlowFuse/flowfuse/issues/515">#515</a></li>
<li>If you are running your own copy of FlowFuse you can now see the version details in the admin pages <a href="https://github.com/FlowFuse/flowfuse/issues/655">#655</a></li>
<li>Device polling is no longer an INFO level message filling the log on your devices <a href="https://github.com/FlowFuse/device-agent/issues/10">#10</a></li>
</ul>
<h2 id="bug-fixes" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/#bug-fixes"></a> Bug Fixes</h2>
<p>We've fixed the following bugs in this release.</p>
<ul>
<li><a href="https://github.com/FlowFuse/device-agent/issues/7">Devices now listen on all Interfaces allowing you to run local http servers</a><br /></li>
<li><a href="https://github.com/FlowFuse/device-agent/issues/7">Solved an issue where a device gets an error unknown device</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/507">The Audit Log in the Forge app displays the correct IP when a user logs in to Node-RED</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/507">Resolved an issue with devices downloading snaphots from legacy stacks</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/735">Fixed an error where objects in the Node-RED log would hang the log page</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/745">Next Billing Date is now shown correctly</a><br /></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/689">Fixed a bug where the loading page would flash during polling</a><br /></li>
</ul>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 0.7 - ready for
you to try out rollbacks and the new theme.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/07/flowforge-07-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.
That also includes if you have any feedback or feature requests.</p>
<p>Customers of FlowFuse Cloud can raise a ticket by emailing support@flowfuse.com</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/FlowFuse 0.6 releasedAdding Devices to the platform2022-06-19T12:00:00ZNick O'Leary<p>Node-RED is well known for its role in IoT solutions - which often means running
flows on devices.</p>
<p>This was something we always wanted to support in FlowFuse and with this release
we're taking the next steps in that direction.</p>
<!--more-->
<h3 id="devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/#devices"></a> Devices</h3>
<p>This release includes the first alpha release of the <a href="https://github.com/FlowFuse/device-agent">FlowFuse Device Agent</a>. This is a small piece of node.js software that can be
installed on a device, such as a Raspberry Pi. It connects back to the FlowFuse
platform to get the Node-RED flows it should be running.</p>
<p>This builds on the work we added in 0.5 that lets you register the device,
generate credentials for it and pick which Project in your team it should be assigned
to.</p>
<p>It makes it super simple to start developing your flows in FlowFuse and push them
out to a group of devices with a couple clicks.</p>
<p>There's plenty of work still to come on the Devices feature. Under the covers
it uses an HTTP polling approach to check for updates. That was a pragmatic choice
to get something working - but it isn't our long term strategy. We'll be working
towards a more IoT-native MQTT/WebSocket appoach in the coming releases.</p>
<ul>
<li><a href="https://flowfuse.com/docs/device-agent/introduction/">Devices documentation</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/446">Epic #446 - Devices</a></li>
</ul>
<h3 id="snapshots" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/#snapshots"></a> Snapshots</h3>
<p>This release adds the ability to create Snapshots of your projects. These are
point-in-time backups of your project's flows, credentials and settings.</p>
<p>With this release we support <em>creating</em> snapshots and pushing them to devices.</p>
<p>We don't have the ability to revert a project back to a previous snapshot, but
that will come soon.</p>
<ul>
<li><a href="https://flowfuse.com/docs/user/snapshots/">Snapshot documentation</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/587">Story #587 - Snapshot/Rollback</a></li>
</ul>
<h3 id="other-updates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/#other-updates"></a> Other updates</h3>
<p>Beyond these headline features, there are a number of smaller, but just as useful
items in this release.</p>
<p>We've continued with the rebranding work started in 0.5 with some more improvements
to the overall UX of the platform. Little touches like placeholder loading graphics
give the UI a more responsive feel.</p>
<p>When you log out of the platform we now also automatically log you out of any
Node-RED editor sessions you have open.</p>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p><a href="https://app.flowfuse.com/">FlowFuse Cloud</a> is already running 0.6 - ready for
you to start creating snapshots and adding devices right now.</p>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/community-news-05/Community News May 2022News from the FlowFuse and Node-RED communities2022-06-17T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/06/flowforge-06-released/">FlowFuse 0.5 AND 0.6</a><br />
We've had two releases since our last newsletter, there are a lot of related features between them.
We introduced a new design for the forge application which aligns with the branding on our website.
We've added <a href="https://flowfuse.com/docs/user/concepts/#device">Devices</a>, allowing you to run and manage your Node-RED projects on your own hardware, this is ideal for applications that need to connect to either sensor data or specialist hardware deployed outside the cloud.
We added a new concept as part of this work, <a href="https://flowfuse.com/docs/user/concepts/#instance-snapshot">Snapshots</a> allow you to take a point in time copy of your project, today that can then be deployed to one or more devices but we have plans to expand this concept for things like <a href="https://github.com/FlowFuse/flowfuse/issues/587">rolling back</a> a project to a previous point in time.
<a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/">0.5</a> Introduced the capabilities of copying a project or certain parts of it, allowing for scenarios like having multiple environments for Development and Production.</p>
<p><a href="https://discourse.nodered.org/t/node-red-3-0-0-beta-3-released/64027">Node-RED 3.0.0-beta-3</a><br />
The Node-RED 3.0 beta releases continue as the project is getting very close to the full release in the coming weeks, We'll be making it availble on FlowFuse cloud very soon.</p>
<p><a href="https://kazuhitoyokoi.medium.com/creating-custom-node-from-subflow-in-node-red-ce52cc42bbba">Creating custom node from subflow in Node-RED</a><br />
One of the contributors to Node-RED, Kazuhito Yokoi wrote a nice tutorial on how to turn a subflow into a custom node.</p>
<p><a href="https://www.youtube.com/channel/UCbBzP8NZbv3WDtlt4UouA-g">YouTube Channel</a><br />
Joe has been busy creating short videos to present FlowFuse and our key concepts, these will start to appear on our YouTube channel, so please like and subscribe!</p>
<p><a href="https://www.42flows.tech/blog/why-have-we-decided-to-implement-visa-direct-api-for-node-red/">Visa Direct in Node-RED</a><br />
The folks over at 42flows use Node-RED in a financial and banking context, they've published an article about integrating with the Visa payments APIs</p>
<p><a href="https://www.papercall.io/nrcon2022">Node-RED Con CFP</a><br />
A reminder about Node-RED Con and the call for papers, submissions close at the end of July but don't wait until the last minute to submit your proposal.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/sign-up-for-flowforge-cloud/FlowFuse open for everybodySign up and start a new Node-RED project within a minute!2022-05-25T09:00:00ZZJ van de Weg<p>FlowFuse wants to enable everyone to build workflows in Node-RED. Since announcing
<a href="https://flowforge.com/blog/2022/02/announcing-flowforge-cloud/">FlowFuse Cloud</a>
two months ago we've had a waiting list for users to sign up to. That allowed us
to control the pace we were bringing new users onto the platform, learning what
is needed to scale up our platform and continue to improve our first user experience.</p>
<p>Today we have removed the waiting list. Anyone can sign up to FlowFuse and start a
new Node-RED project in under a minute!</p>
<!--more-->
<div class="max-w-md m-auto">
<a class="ff-btn ff-btn--primary" href="https://app.flowfuse.com/account/create">Sign up</a>
</div>
<h2 id="what-we-offer" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/sign-up-for-flowforge-cloud/#what-we-offer"></a> What we offer</h2>
<p>Besides the sub minute time to start a new Node-RED project, there's many more
features our offering includes. Two we'd like to highlight.</p>
<p>To start our blog highlight reel: Collaboration. FlowFuse allows you to work
with a team on your flows. There's the ability to create multiple users, each
with their own credentials that can alter the flows on Node-RED, and it's
execution environment like for example the <a href="https://flowforge.com/docs/user/envvar/">environments variables</a>.</p>
<p>Furthermore, <a href="https://flowforge.com/docs/user/changestack">stacks</a>. These allow
a user to select the execution environment for their Node-RED project. For example; the
Node-RED version being used. Combined with the ability for one to copy a project
to a new stack, this allows FlowFuse users to copy their project to the Node-RED
3.0-beta stack to validate their solutions will continue to work on the new release
without disrupting their main project.</p>
<h2 id="our-roadmap" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/sign-up-for-flowforge-cloud/#our-roadmap"></a> Our roadmap</h2>
<p>Currently we're working towards our 0.6 release. The main feature of this release
will be support for Devices. This will allow you to send a snapshot of a project
to a Node-RED instance running outside of the FlowFuse platform and update the flows
remotely. Remote devices will run our <a href="https://github.com/FlowFuse/device-agent">agent</a>
to communicate with the FlowFuse Cloud project.</p>
<p>While the first iteration will be considered an Alpha release, by shipping early and often, it lets us get
welcome feedback from our users and the wider community - helping to shape the future direction.
It also allows users to start validating the feature for their own proof of concept projects.</p>
<p>Over the next few months we're continuing to drive development of the platform
across a number of areas - including further improvements to the Device feature.
But also looking at new Enterprise-ready features, such as Single-Sign On integration
and more tools to make collaboration even easy.</p>
<p>We intend to grow our offering so that FlowFuse remains the best way to run
Node-RED.</p>
<p>Stay informed by registering for our newsletter!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/FlowFuse 0.5 releasedBringing a new look to the platform2022-05-12T12:00:00ZNick O'Leary<p>The cycle continues with our next regularly scheduled release, bringing a fresh
new look to the platform.</p>
<!--more-->
<p>Since joining the team, Joe has been hard at work bringing a more consistent
design language to what we're doing. This release brings a lot of his hardwork
to the platform itself.</p>
<p>There's more to be done on the individual pages of the platform, but this
gives us a solid framework to build on.</p>
<p><picture><source type="image/avif" srcset="https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-650.avif 650w, https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-1300.avif 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/webp" srcset="https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-650.webp 650w, https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-1300.webp 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><source type="image/jpeg" srcset="https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-650.jpeg 650w, https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-1300.jpeg 1300w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img alt="" loading="lazy" decoding="async" src="https://flowfuse.com/img/ff-05-dashboard-nX3Q-3e2fZ-650.jpeg" width="1300" height="827" /></picture></p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/430">Epic #430 - Rebrand Forage App</a></li>
</ul>
<h3 id="copying-projects" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/#copying-projects"></a> Copying Projects</h3>
<p>One of the usage scenarios we want to support is having an easy way to have separate
test and production environments. The previous release added the ability to configure
environment variables on individual projects.</p>
<p>This release unlocks the next piece of the puzzle - making it easy to copy flows
between projects.</p>
<p>The Project settings page has two new options:</p>
<ul>
<li>Copy Project lets you create a complete copy of the project.</li>
<li>Export to existing project lets you copy over selected aspects of the project
over to another project.</li>
</ul>
<p>In both cases, you get to pick what parts of the project should be copied.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/268">Epic #268 - Export Project</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/271">Story #271 - Duplicate Project</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/272">Story #272 - Export to Existing Project</a></li>
</ul>
<h3 id="improve-billing-information" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/#improve-billing-information"></a> Improve Billing Information</h3>
<p>We've been getting some great feedback from the users of FlowFuse Cloud. One of the
areas we identified as needing some more clarity was around the point users are
asked to setup their billing information.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/563">Story #563 - Improve Information on billing</a></li>
</ul>
<h3 id="edge-devices" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/#edge-devices"></a> Edge Devices</h3>
<p>Whilst we always want to deliver new functionality to end users in each release,
sometimes bits of work don't fit naturally into a single four week iteration.</p>
<p>That's the case here with some of the preliminary work we've done to introduce
the idea of Edge Devices to the platform.</p>
<p>The goal here is to provide a way to easily deploy and manage Node-RED projects
on remote devices.</p>
<p>This release introduces a bunch of work to the core app and front-end to begin
introducing the concept of a Device. It includes the basic workflows for registering
a device on the platform and being able to assign it to a team.</p>
<p>The whole feature is hidden behind a feature flag so users on FlowFuse Cloud won't
see any of this quite yet.</p>
<p>The next release will introduce the Edge Agent piece of this - the bit that runs
on devices.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/446">Epic #446 - Devices</a></li>
</ul>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/flowforge-05-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/community-news-04/Community News April 2022News from the FlowFuse and Node-RED communities2022-05-06T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/">FlowFuse 0.4</a><br />
The next release has enabled us to offer multiple versions of Node-RED along with adding more features to help you configure your Node-RED projects.</p>
<p><a href="https://discourse.nodered.org/t/node-red-3-0-0-beta-1-released/62124">Node-RED 3.0.0-beta-1</a><br />
The first beta for Node-RED 3.0.0 has been published, there's some exciting improvements on UI in the editor to make designing your flows even easier.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/node-red-3-beta-stack/">Node-RED Beta on FlowFuse</a><br />
As promised in out 0.4 announcement, we've made the beta available to our users on FlowFuse as a separate stack, this is just one way that we're able to demonstrate the flexibility you get from running Node-RED on FlowFuse.</p>
<p><a href="https://developer.cisco.com/meraki/build/exploring-meraki-and-spark-apis-with-node-red/">Cisco & Node-RED</a><br />
The team at Cisco DevNet published some great guides on using Node-RED to manage both your Meraki Access points and to integrate with WebEx Teams, they've also produced a handy <a href="https://blogs.cisco.com/developer/helloworldlowcodenodered01">starter guide</a> for those that are new to Node-RED</p>
<p><a href="https://nrcon.nodered.org/">Node-RED Con 2022</a><br />
Our friends in the Node-RED Japan User Group have run a number of successful Node-RED conferences over the last few years. This year, we're joining forces with them to bring the event to a wider audience. The <a href="https://www.papercall.io/nrcon2022">Call for Papers</a> is open now and we'd love to see your submissions.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/05/node-red-3-beta-stack/Node-RED 3.0 Beta StackTry out the next major Node-RED release2022-05-04T00:00:00Z<p>The first beta of Node-RED 3.0 is here and FlowFuse is ready for you to try it out.</p>
<!--more-->
<p>When we released <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/">FlowFuse 0.4</a> last month we talked about allowing users to select the stack their project runs on.
Until now we've only offered one stack which has been the latest Node-RED release (2.2.2).</p>
<p>Yesterday the first beta of Node-RED 3.0 was <a href="https://discourse.nodered.org/t/node-red-3-0-0-beta-1-released/62124">released</a>, so as of today we have added a choice of stacks to FlowFuse Cloud. You can stick with the <em>Default</em> and use Node-RED 2.2.2 or if you want to try out the beta you can select <em>Node-RED-3.0.0-beta-1</em>.</p>
<p><picture><source type="image/gif" srcset="https://flowfuse.com/img/beta_stack-qr0eO95s0v-650.gif 650w" sizes="(min-device-pixel-ratio: 1.25) 1300px, (min-resolution: 120dpi) 1300px, 650px" /><img title="Selecting the beta Stack" alt="Selecting the beta Stack" loading="lazy" decoding="async" src="https://flowfuse.com/img/beta_stack-qr0eO95s0v-650.webp" width="650" height="497" /></picture></p>
<p>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.</p>
<p>We'll continue to update the stack choice with each beta when they are released.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/FlowFuse 0.4 releasedGetting ready for Node-RED 3.02022-04-14T12:00:00Z<p>This release of the FlowFuse adds a seemingly small, but significant new feature.</p>
<!--more-->
<p>With <a href="https://nodered.org/about/releases/">Node-RED 3.0 fast approaching</a> we've been making sure we are ready to support this.</p>
<h3 id="upgrading-node-red" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#upgrading-node-red"></a> Upgrading Node-RED</h3>
<p>The goal of FlowFuse is to be the best way to run Node-RED at any scale, whether that's many users or many instances. Node-RED is a constantly developing as a platform and therefore part of running Node-RED is also upgrading the version you are running.</p>
<p>With the 0.4 release today we've made that super simple in FlowFuse. Last month we introduced the concept of <a href="https://flowfuse.com/docs/user/concepts/#stack">Project Stacks</a>. One of the key elements of a Stack was the version of Node-RED in use. Initially this may have seemed fairly basic, when you create a new project you usually want to use the latest version of Node-RED. However what happens when a new version is released and you have an existing project?
Now you can change the stack that a project is running on, which in turn will change the version of Node-RED. This is a simple process from the project settings, it only requires a short period of downtime while the project restarts on the new stack, typically around 10-15 seconds.</p>
<p>Our driver to get this feature into the 0.4 release is the approaching release of Node-RED 3.0, now we know that we can be ready to offer our users Node-RED 3.0 as soon as it is released.</p>
<p>We will also be making available the Beta's of Node-RED 3.0 within FlowFuse Cloud, this becomes a great way to test out the new features without having to touch your own environments.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/288">Story #288 - Change Stacks</a></li>
<li><a href="https://flowfuse.com/docs/user/changestack/">Docs</a></li>
</ul>
<h3 id="environment-variables" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#environment-variables"></a> Environment Variables</h3>
<p>Another key new feature we are introducing is the ability to set and manage environment variables within your projects.
Environment Variables are a key tool when building applications as they allow you to to separate the configuration of your system from the logic in the code. Even in Low-Code platforms this is an important design pattern. Environment variables are fully integrated into <a href="https://flowfuse.com/docs/user/concepts/#template">Templates</a> that we introduced last month so they can be set both at the platform level or on an individual project.
Our plans for the next release will make these even more useful as we introduce the ability to <a href="https://github.com/FlowFuse/flowfuse/issues/271">duplicate a project</a> and then modify those variables for the new project.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/225">Story #225 - Project Environment Variables</a></li>
<li><a href="https://flowfuse.com/docs/user/envvar/">Docs</a></li>
</ul>
<h3 id="there's-more" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#there's-more"></a> There's more</h3>
<p>There are many more improvements in this release, such as the ability to <a href="https://github.com/FlowFuse/flowfuse/issues/239">Set the timezone</a> your project is running in, we've also been iterating on our billing experience as we've welcomed the first paying customers to FlowFuse Cloud.</p>
<p>Finally we're very happy that we've had our first external contribution to the code base, as an Open Core company we believe strongly that Open Source lives at the heart of everything we do.
We would like to say a big thank-you to <a href="https://fakocodes.netlify.app/">Fakorede Damilola Idris</a> for his work on fixing a <a href="https://github.com/FlowFuse/flowfuse/issues/424">bug</a> in the UI.</p>
<h3 id="getting-started-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#getting-started-with-flowfuse"></a> Getting started with FlowFuse</h3>
<p>The documentation provides a guide for <a href="https://flowfuse.com/docs/install/">installing FlowFuse on a local server</a>.</p>
<p>If you haven't played with FlowFuse yet, here's a more complete walk-through
of the platform:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/YYZDx8n17Ys" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/#upgrading-flowfuse">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-04-released/#what's-next%3F"></a> What's next?</h3>
<p>Our regular release cycle puts the next release on Thursday 12th May.
We will be building on features in the last few releases around managing your projects and using templates, we're also setting the foundations of our work to <a href="https://github.com/FlowFuse/flowfuse/issues/446">manage Node-RED on your own devices running at the Edge</a>.</p>
<p>For more information, check out the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/">announcement blog post</a>.</p>
<p>You can also sign up to our general mailing list below if you want to hear more
about the work we're doing.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/community-news-03/Community News March 2022News from the FlowFuse and Node-RED communities2022-04-05T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="https://flowfuse.com/">New Website</a><br />
Some of you may have noticed the new design on our website, this is the first step in our more refined corporate identity, the same look will be coming to the FlowFuse application <a href="https://github.com/FlowFuse/flowfuse/issues/430">soon.</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/">FlowFuse 0.3</a><br />
The latest release introduced 2 new concepts, Templates and Stacks, with these we are starting to show the value of the FlowFuse platform allowing you to easily manage the Node-RED settings and versions used by your projects.</p>
<p><a href="https://www.opto22.com/support/resources-tools/videos/video-introduction-to-json-for-node-red/">Intro to JSON for Node-RED</a><br />
Our friends over at Opto 22 published a handy guide to JSON and how it works in Node-RED, great introduction for new builders and a good refresher for seasoned pros.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/04/flowforge-accepting-customers/FlowFuse is accepting customers nowWe're starting to onboard users from the waitlist2022-04-04T00:00:00ZZJ van de Weg<p>A year ago our CTO, Nick O'Leary, <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/04/first-deploy/">introduced FlowFuse</a>.
Since then major milestones have been achieved. As we grow as a company, important
steps are taken. Today we make another very important step; we're accepting our
first customers.</p>
<!--more-->
<p>A few weeks ago our product was nearly in a state where it could support customers,
when that was the case, we <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/">opened up our waitlist</a>.
The waitlist has been growing daily and now we're ready to start inviting those
users onto the platform. The first
lucky few users are being added today, and each working day for the foreseeable
future we'll continue onboarding users. New users will receive an email from our
team with their login details and can start creating new workflows with Node-RED
minutes after.</p>
<p>While the source code of FlowFuse is <a href="https://github.com/FlowFuse/flowfuse">available</a>,
there's a chance you're unfamiliar with what has been built around Node-RED.
With FlowFuse, our intent is to build a platform to aid with colaboration of
flows in Node-RED. The first steps to our vision include a managed Node-RED
instance to connect virtually any online service. Multiple users will have
access to the same flows and can collaborate. Further, once Node-RED 3.0 has
been released, the platform will provide a pain-free way to upgrade and keep
your projects up to date. There's many more exciting features right around the
corner on our <a href="https://flowfuse.com/changelog/">roadmap</a></p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/FlowFuse 0.3 releasedMoving towards the launch of FlowFuse Cloud2022-03-17T01:00:00ZNick O'Leary<p>The FlowFuse 0.3 release brings us closer to the launch of FlowFuse Cloud.
Find out more about what's in this new release.</p>
<!--more-->
<p>This release of the FlowFuse platform brings some significant new features that
will underpin more of what is to come.</p>
<h3 id="project-stacks-%26-templates" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#project-stacks-%26-templates"></a> Project Stacks & Templates</h3>
<p>When we think about what a makes a Project inside FlowFuse, the simple answer
is Node-RED.</p>
<p>The more complete answer is: a version of Node-RED, a version of Node.js, some
memory, some CPU and a bunch of Node-RED settings to customise the instance.</p>
<p>In a platform like FlowFuse, it's important to have the tools to manage all
of these things.</p>
<p>This is where Project Stacks and Template come in.</p>
<p>A Project Stack defines the underlying characteristics of the Node-RED process -
or the container it is running in. For example, with our Local deployment model,
it defines the version of Node-RED to use and how much memory the process should
try to use. In our container based deployment models, the stack identifies the
container to use for the project, along with memory and CPU limits.</p>
<p>In a future release, this will be the way we will support upgrading the version
of Node-RED a project is using - and doing so in a well managed way. An Administrator
will be able to create a new Stack containing the new version of Node-RED.
Project owners will then be able to update their projects to use the new Stack -
at a time that is convenient to them.</p>
<p>A Project Template is more about how the Node-RED instance is configured - exposing
the options a user would traditional modify in their Node-RED settings file.
With this release, we're not exposing a lot of settings as the focus has been
more on the underlying Template concept. But it will be the basis for gradually
exposing more options for customisation in the future.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/285">Epic #285 - Project Stacks</a></li>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/141">Epic #141 - Project Templates</a></li>
</ul>
<h3 id="billing-integration" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#billing-integration"></a> Billing Integration</h3>
<p>With our open core philosophy, the heart of the FlowFuse platform is open source
and available under the Apache 2 license for anyone to use.</p>
<p>But the plan was always to have certain features that were licensed separately.</p>
<p>This release brings the first of those features - Stripe Billing Integration. This
feature brings the ability to require a Team to have a Stripe Billing agreement
in place and to be able to charge on a per-project basis within that Team.</p>
<p>Being able to charge is an important feature for any commercial platform, and
with our own FlowFuse Cloud launching soon, we needed to get this feature in
place today.</p>
<p>We've structured the code in the repository and updated the LICENSE file to make it
very clear what parts of the code base are <em>not</em> covered by the Apache 2 license.</p>
<ul>
<li><a href="https://github.com/FlowFuse/flowfuse/issues/224">Epic #224 - Billing</a></li>
</ul>
<h3 id="getting-started-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#getting-started-with-flowfuse"></a> Getting started with FlowFuse</h3>
<p>The documentation provides a guide for <a href="https://github.com/FlowFuse/flowfuse/tree/main/docs">installing FlowFuse on a local server</a>.</p>
<p>If you haven't played with FlowFuse yet, here's a more complete walk-through
of the platform:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/YYZDx8n17Ys" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>If you installed FlowFuse 0.1 or 0.2 and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/flowforge-03-released/#what's-next%3F"></a> What's next?</h3>
<p>Our regular release cycle puts the next release on Thursday 14th April.</p>
<p>We're still in planning stage for the release, but we'll also be beginning to invite
people from the waiting list to sign-up to FlowFuse Cloud.</p>
<p>For more information, check out the <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/">annoucement blog post</a>.
You can also sign up to our general mailing list below if you want to hear more
about the work we're doing.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/03/community-news-02/Community News February 2022News from the FlowFuse and Node-RED communities2022-03-02T00:00:00Z<p>Welcome to the FlowFuse newsletter, a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community.</p>
<!--more-->
<p>If you've got something that you'd like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/">Announcing FlowFuse Cloud</a><br />
We are excited to announce FlowFuse Cloud, a hosted Node-RED as a service offering and today we are opening the waitlist.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/">FlowFuse 0.2</a><br />
We continue to iterate with our 4 weekly releases of the FlowFuse platform.</p>
<p><a href="https://discourse.nodered.org/t/node-red-2-2-2-released/58606">Node-RED 2.2.2</a><br />
The 2.2 release of Node-RED (last month) has received 2 maintenance releases to fix bugs with duplicate wires in the editor and MQTT.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/welcome-joe/">New Team Members</a><br />
FlowFuse is now up to 6 people, Joe Pavitt has joined the team</p>
<p><a href="https://boards.greenhouse.io/flowfuse/jobs/4312861004">We still are Hiring</a><br />
We're looking for the next member of our team, If you're a Node.JS developer and want to work with us take a look at the link.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/Announcing FlowFuse CloudHosting your Node-RED, so you don't have to.2022-02-23T19:44:00ZZJ van de Weg<p>As an open core company, anyone is free to <a href="https://github.com/FlowFuse/flowfuse/tree/9219e81399eaf52fb0ee5573707a52f5520fbfdd/docs/install">download and install</a>
our platform. In many cases this is a great solution, it
allows for custom setups in your own environment. We know this isn't for everyone
though, some people just want to start building with Node-RED without having to manage their servers.</p>
<p>We are excited to announce FlowFuse Cloud, a hosted
Node-RED as a service offering and today we are opening the waitlist.</p>
<!--more-->
<p>Our waitlist captures your email, and we'll reach out to you on that address once your account is created.</p>
<h3 id="starting-operations" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/announcing-flowforge-cloud/#starting-operations"></a> Starting operations</h3>
<p>After having released v0.2 recently, we're now working on v0.3 that will include
a user flow for <a href="https://github.com/FlowFuse/flowfuse/issues/224">billing</a>.
When that work has been done and deployed people on the waitlist will slowly be invited to the platform.
Currently that's scheduled for April 1st, no joke, although it could happen either sooner
or later.
More details on the exact pricing will be availble nearer the time.</p>
<p>Once you are invited you will be able to;</p>
<ul>
<li>Create multiple Node-RED projects hosted on flowforge.cloud,</li>
<li>Invite team members to collaborate on those projects,</li>
<li>And many more features will automatically become availble with each new release.</li>
</ul>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/FlowFuse 0.2 releasedKeeping the releases flowing of our open platform for Node-RED2022-02-17T01:00:00ZNick O'Leary<p>Four weeks have passed since our initial release of FlowFuse, and we're happy
to release v0.2 today as we continue moving forward and evolve the platform.</p>
<!--more-->
<p>There aren't lots of headline features in this release to tell you about as a lot
of the work has been on the internals, as well as responding to some of the early
feedback from the community.</p>
<p>Features like improving the test framework, and building a database migration
framework may not sound too exciting to the end user, but they are critical pieces
when build a platform that needs to be stable and easy to upgrade.</p>
<p>We've also been doing work to get our own instance of the platform running in the
Cloud - and figuring out how to automate as much of that as possible. Aside
from being a key way to test the platform, it helps validate the work we're doing
for when others come to run it in that way. It also lays the ground work for our
own cloud service we'll be sharing more about in the coming days.</p>
<p>The full change-log for the core of the platform is available <a href="https://github.com/FlowFuse/flowfuse/blob/v0.2.0/CHANGELOG.md">on GitHub</a>.
But with a further 15 repositories containing different components, each with its
own change-log, we're still thinking about how best to share a single view of the
updates.</p>
<h3 id="getting-started-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/#getting-started-with-flowfuse"></a> Getting started with FlowFuse</h3>
<p>The documentation provides a guide for <a href="https://github.com/FlowFuse/flowfuse/tree/main/docs">installing FlowFuse on a local server</a>.</p>
<p>If you haven't played with FlowFuse 0.1 yet, here's a more complete walk-through
of the platform:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/YYZDx8n17Ys" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h3 id="upgrading-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/#upgrading-flowfuse"></a> Upgrading FlowFuse</h3>
<p>If you installed FlowFuse 0.1 and want to upgrade, our documentation provides a
guide for <a href="https://flowfuse.com/docs/upgrade/">upgrading FlowFuse on a local server</a>.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<p>We also have a <code>#flowfuse</code> channel on the <a href="https://nodered.org/slack">Node-RED Slack workspace</a>.</p>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/flowforge-02-released/#what's-next%3F"></a> What's next?</h3>
<p>Our regular release cycle puts the next release on Thursday 17th March. We've
got some key features planned in this release around <a href="https://github.com/FlowFuse/flowfuse/issues/141">Project Templates</a> and <a href="https://github.com/FlowFuse/flowfuse/issues/285">Stacks</a> -
which will underpin how you can customise Node-RED within FlowFuse.</p>
<p>We'll also have some exciting news to share about our own hosted service you'll
be able to sign-up for.</p>
<p>Sign up to the mailing list below if you want to hear more about the work we're
doing.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/use-case-solar-afloat/Using Node-RED to keep Solar PV afloatHow spb sonne used Node-RED with a renewable energy solution2022-02-09T09:26:00ZZJ van de Weg<p><a href="https://www.linkedin.com/in/krishnanravichandran/">Krisnan Ravichandran</a> works
for <a href="https://www.sbp.solar/">spb sonne</a>, a engineering consultancy for renewable
energy. Within the company he is part of the engineering effort on the
<a href="https://www.sbp.de/en/news/goembhal-sbp-sonnes-pioneering-floating-pv-system/">Gömbhal</a>
project, creating a floatation device for solar panels.</p>
<p>In this post, he shares his experiences of using Node-RED.</p>
<!--more-->
<p>Currently there’s a prototype deployed in Hungary, while the company is located
in Stuttgart: “We’re remotely monitoring the installation. There are over 40
sensors that all connect to an ADAM-6717, Compact Intelligent Gateway. When the
data is acquired we leverage Node-RED flows that maintain the structure, monitor
performance, and provide reporting back to our offices.</p>
<p>Node-RED is embedded in the ADAM 6717, it was very new to me. I was already
experienced in programming, mainly Python, and within a month I felt very
comfortable and productive in Node-RED. Understanding programming is useful,
though not a necessity.</p>
<p>Building and improving flows I did on my own through trial-and-error, as well
as a lot of times through help on the <a href="https://discourse.nodered.org/">Node-RED Forum</a>.
The community is helpful and welcoming to new users. Now I maintain multiple
flows with very different purposes. Some track temperature,
irradiation wind-speed, direction, tilt and wave height; to ensure the floating
PV installation remains floating. Other sensors are connected to actuators
through flows that control pressure.</p>
<p>We do have some challenges; the ADAM 6717 contained an older version of Node-RED.
This raised questions around security and maintenance, as our Node-RED version
isn’t updated to a newer version in an easy manner. It also hampers training a
bit because documentation might reference an API or node for a flow that’s just
not the same on older versions.</p>
<p>However, I’d choose Node-RED again, it’s well known as well as easy to learn.
Furthermore I found it very versatile.”</p>
<hr />
<p>Thanks to Krisnan for sharing his story. If you have a Node-RED story for us
to share, please get in touch via <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/02/welcome-joe/Welcome Joe2022-02-08T00:00:00ZNick O'Leary<p>Today we welcome Joe Pavitt (<a href="https://twitter.com/joepavitt3d">@joepavitt3d</a>) as
our new Head of UX & Design. This is a key role that will help deliver the awesome
user experience of the FlowFuse platform.</p>
<!--more-->
<p>Joe has a passion for user experience, data visualisation and creativity in
technology. He joins us having been at IBM for 9 years where he specialised in
building bespoke, first of a kind experiences in IBM's Emerging Technology and
Research teams.</p>
<p>We worked together at IBM and I saw first-hand the range and quality of what he
can do. I was super pleased when he agreed to join us and I look forward to
seeing the real impact he'll have on what we're building.</p>
<p>Welcome aboard Joe!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/community-news-01/Community News January 2022News from the FlowFuse and Node-RED communities2022-01-28T00:00:00Z<p>Welcome to the first FlowFuse newsletter, we’re going to publish this as a regular roundup of what`s happening with both FlowFuse and the wider Node-RED community, if you want to receive it via email, sign up for updates at the bottom of the page.</p>
<!--more-->
<p>If you’ve got something that you’d like us to share please email <a href="mailto:contact@flowfuse.com/">contact@flowfuse.com</a>.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/">FlowFuse 0.1 Released</a><br />
First up we are really pleased to ship the first version of our platform, this is a very early release but hopefully will give you an idea of the direction we’re going in.</p>
<p><a href="https://nodered.org/blog/2022/01/27/version-2-2-released">Node-RED 2.2</a><br />
The next version of Node-RED has been released with new editor features, predefined environment variables and improvements to some of the core nodes. Checkout the blog post and change log for more details.</p>
<p><a href="https://blog.golioth.io/building-iot-dashboards-with-golioth-grafana-and-node-red">Make your IoT data beautiful</a><br />
Ben Mawby wrote a guide on connecting Golioth's WebSocket endpoints to Grafana using Node-RED and InfluxDB</p>
<p><a href="https://www.sammachin.com/posts/alexaweb-reborn">Alexa Voice Service on Node-RED</a><br />
Sam Machin rebuilt an app on Node-RED allowing you to talk to Alexa through the browser. And he has published a new <a href="https://flows.nodered.org/node/@sammachin/node-red-alexa-voice-service">node</a> to use the Alexa Voice Service within your own flows.</p>
<p><a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/">New Team Members</a><br />
We welcomed 2 new members of the FlowFuse team this month <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/welcome-zj/">ZJ</a> joins as our CEO and <a href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/welcome-steve/">Steve</a> has come onboard to work on the Node-RED project</p>
<p><a href="https://boards.greenhouse.io/flowfuse/jobs/4312861004">We are Hiring</a><br />
We're looking for the next member of our team, If you're a Node.JS developer and want to work with us take a look at the link.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/FlowFuse 0.1 releasedMaking the first release of the platform and transitioning to open development2022-01-20T01:00:00ZNick O'Leary<p>For an open core company, we haven't been very open with what we're doing. That all
changes today with the release of FlowFuse 0.1 and making all of our repositories
public.</p>
<p>This is a significant step for the company as we look to build a platform around Node-RED.</p>
<!--more-->
<p>The main question we get asked is: 'What is FlowFuse?" - which is a very reasonable
question to ask.</p>
<p>FlowFuse is a platform for managing Node-RED instances at scale. It lets you have
multiple users on the platform, organised into teams to provide proper access control
to individual Node-RED instances, or Projects as we call them.</p>
<p>With the 0.1 release, we have the basic building blocks of the platform in place.</p>
<ul>
<li>Add multiple users to the platform</li>
<li>Create teams for those users</li>
<li>Create Node-RED projects quickly and easily through the platform UI</li>
</ul>
<p>For a more complete walk-through of the platform in this early release, you can
watch this video.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/YYZDx8n17Ys" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<h3 id="getting-started-with-flowfuse" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/#getting-started-with-flowfuse"></a> Getting started with FlowFuse</h3>
<p>The documentation provides a guide for <a href="https://github.com/FlowFuse/flowfuse/tree/main/docs">installing FlowFuse on a local server</a>.</p>
<p>We also have drivers for deploying to Docker Compose and Kubernetes based environments
to enable a larger scale of deployment. We'll have more documentation on those options
in the near future.</p>
<h3 id="getting-help" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/#getting-help"></a> Getting help</h3>
<p>If you hit any problems with the platform, or have questions to ask, please do
raise an <a href="https://github.com/FlowFuse/flowfuse/issues">issue on GitHub</a>.</p>
<p>That also includes if you have any feedback or feature requests.</p>
<h3 id="what's-next%3F" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/flowforge-01-released/#what's-next%3F"></a> What's next?</h3>
<p>With so much in the plan and lots of exciting features to come, we will have a
regular cycle of releases every four weeks. So you can expect the next release, 0.2,
on Thursday 17th February.</p>
<p>As we're only at 0.1 today, there is a lot still to do. We will be following the
principles of <a href="https://semver.org/">Semantic Versioning</a> with our releases,
but until we reach 1.0, there may be some disruptive changes along the way.</p>
<p>Sign up to the mailing list below if you want to hear more about the work we're
doing.</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/welcome-steve/Welcome Steve2022-01-20T00:00:00ZNick O'Leary<p>We're continuing to grow the FlowFuse team with our latest hire; Steve McLaughlin
who is joining our development team.</p>
<!--more-->
<p>Steve is a well known face in the Node-RED community. He is a regular contributor
to the community forum, always happy to help users with their questions.</p>
<p>He has published a number of very popular nodes, include <a href="https://flows.nodered.org/node/node-red-contrib-buffer-parser">buffer-parser</a>,
<a href="https://flows.nodered.org/node/node-red-contrib-cron-plus">cron-plus</a> and
<a href="https://flows.nodered.org/node/node-red-contrib-image-tools">image-tools</a>.</p>
<p>He has also made some significant contributions to the core of Node-RED, such as
delivering MQTTv5 support and introducing the Monaco code editor into the heart
of the editor.</p>
<p>Steve joins us from a background in Industrial IoT in the automative manufacturing
space - experience and knowledge that will be invaluable as we look to growing the
FlowFuse platform.</p>
<p>Steve will be focussed on the Node-RED side of our activities - helping to continue
the ongoing development and growth of the open source project at the heart of what
FlowFuse is about.</p>
<p>Welcome aboard Steve!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2022/01/welcome-zj/Welcome ZJ2022-01-03T00:00:00ZNick O'Leary<p>This year the FlowFuse team will grow further, and I'm excited to announce our
newest addition to the team; Zeger-Jan van de Weg (<a href="https://twitter.com/ZJvandeWeg">@ZJvandeWeg</a>).
Zeger-Jan, also known as ZJ, is joining FlowFuse as CEO.</p>
<!--more-->
<p>ZJ previously worked at GitLab where he helped build a vibrant open source
community and we're thrilled to have him to continue this with the Node-RED
community. Under his supervision the involvement of GitLab in the Git project
grew significantly, allowing Git and GitLab to be successful together. This aligns
well with our vision for FlowFuse:</p>
<blockquote>
<p>We will only be successful if the whole Node-RED community is successful.</p>
</blockquote>
<p>As our first non-engineering hire, ZJ will be focussed on growing the business side
of FlowFuse, working on marketing and ongoing business development.</p>
<p>Welcome aboard ZJ!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/05/welcome-ben/Welcome Ben2021-05-10T00:00:00ZNick O'Leary<p>I'm excited to share the news that Ben Hardill (<a href="https://twitter.com/hardillb">@hardillb</a>) is joining FlowFuse as a Senior Engineer.</p>
<!--more-->
<p>I've known Ben for many years, having worked together at IBM. He's been an active member
of the Node-RED community since the start of the project and he published one of
the very first <a href="https://flows.nodered.org/node/node-red-node-geofence">3rd party nodes</a>.</p>
<p>He is ever-present on Stack Overflow, to the point where I've long since stopped
rushing to respond to questions tagged with <a href="https://stackoverflow.com/questions/tagged/node-red"><code>node-red</code></a>
in the full knowledge that he has usually beaten me to it.</p>
<p>More recently he's been doing some really interesting work exploring <a href="https://www.hardill.me.uk/wordpress/2020/10/01/multi-tenant-node-red/">multi-tenant Node-RED systems</a> - something we'll
be continuing at FlowFuse.</p>
<p>Welcome aboard Ben!</p>
../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/04/first-deploy/Introducing FlowFuse Inc.Building a new low-code development platform around the Node-RED project2021-04-06T00:00:00ZNick O'Leary<p>When Dave and I first created <a href="https://nodered.org/">Node-RED</a>, it was a tool to solve a
problem - allowing us to do our day job more effectively when building IoT solutions
for clients. That gave us the means and purpose to create a truly useful platform.</p>
<p>When it became an open source project, it quickly found an enthusiastic audience that
has seen the community grow beyond our imagination. From both individual users, to a wide
range of companies integrating it into their own products.</p>
<p>But with that growth, the question in my mind has always been how to take it further
and secure its long term future.</p>
<p>I wrote on the <a href="https://nodered.org/blog/2020/10/13/future-plans">project blog</a>
last year about the future plans of the project. A key piece of that is its sustainability -
how we can increase the commercial adoption of Node-RED and how we can get more people
contributing back.</p>
<p>An opportunity presented itself earlier this year that I believe will bring a
step-change to what the Node-RED project is able to achieve.</p>
<!--more-->
<h3 id="introducing-flowfuse-inc." tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/04/first-deploy/#introducing-flowfuse-inc."></a> Introducing FlowFuse Inc.</h3>
<p>So today, I'm launching FlowFuse Inc - a new company whose mission is to build a low-coding development
platform fit for the enterprise with Node-RED at its heart.</p>
<p>Backed by <a href="https://www.linkedin.com/in/sijbrandij/">Sid Sijbrandij</a>, we have funding
in place to create a fully remote team dedicated to an open core model.</p>
<p>In the short term, this means helping to accelerate the plans already in place for
the Open Source project. Getting the 2.0 release done in the next few weeks, working on
long-standing features such as the Test framework and Flow Debugger.</p>
<p>Alongside that we'll also be building a platform around Node-RED that will make it easier
to adopt at scale and integrate into existing enterprise environments.</p>
<p>Node-RED remains a fully open source project, with its home at the OpenJS Foundation
and an open governance model that allows anyone to have a say in its development.</p>
<p>Our goal is to incorporate as much of our work directly into the core project as possible. Where we do create closed-source components, we will work with the community to ensure the right APIs and extension points are in the core for all to benefit from.</p>
<p>We will only be successful if the whole Node-RED community is successful.</p>
<p>For me personally, this is a really exciting next step. I never expected to turn my little side project into a full-time job and now into a company.</p>
<h3 id="hiring-soon!" tabindex="-1"><a class="header-anchor" href="../nopathsource/98e7df1a1dd220ec540da9341c77dc8d/2021/04/first-deploy/#hiring-soon!"></a> Hiring soon!</h3>
<p>We'll be hiring soon - so keep an eye out if you're interesting in getting involved.</p>