[{"data":1,"prerenderedAt":12933},["ShallowReactive",2],{"blog-\u002Fblog\u002Fai":3,"blog-all-for-related":4,"blog-all-ai":5},null,[],[6,228,534,616,817,994,1154,5555,5852,6003,6177,6342,6537,6636,8211,8327,8443,8721,8956,9168,9348,9464,9795,10055,11641,12214,12302,12424,12467,12594],{"id":7,"title":8,"authors":9,"body":11,"cta":201,"date":205,"description":206,"extension":207,"image":208,"lastUpdated":209,"meta":210,"navigation":216,"path":217,"seo":218,"sitemap":219,"stem":220,"subtitle":221,"tags":222,"tldr":225,"video":226,"__hash__":227},"blog\u002Fblog\u002F2026\u002F05\u002Fflowfuse-expert-building-flows.md","How to Build Industrial Apps With FlowFuse AI Expert",[10],"sumit-shinde",{"type":12,"value":13,"toc":186},"minimark",[14,18,27,38,43,46,80,83,87,95,103,106,111,114,117,123,126,132,136,139,142,147,150,154,157,160,164,167,171,174,178],[15,16,17],"p",{},"FlowFuse Expert now builds applications for you. Describe what you need, and the flow is built in front of you on the canvas, wired and configured. Ask for a change, it updates on the spot.",[15,19,20,21,26],{},"We shared the initial announcement in the ",[22,23,25],"a",{"href":24},"\u002Fblog\u002F2026\u002F05\u002Fflowfuse-release-2-30\u002F#expert-application-building","2.30 release post",". This post walks through building your first flow with FlowFuse Expert and how Expert works alongside your environment.",[15,28,29,34],{},[30,31],"img",{"alt":32,"src":33},"FlowFuse Expert building a simulated packaging conveyor monitoring application","\u002Fblog\u002F2026\u002F05\u002Fimages\u002Fexpert-application-building.gif",[35,36,37],"em",{},"Expert building a packaging conveyor monitoring application: MQTT alerts, dashboard indicators, and real-time event simulation, from a single prompt.",[39,40,42],"h2",{"id":41},"what-flowfuse-expert-sees-before-it-builds","What FlowFuse Expert sees before it builds",[15,44,45],{},"Before the first node lands on the canvas, Expert reads your environment. We're tuning the experience to load context agentically based on what each scenario needs. Depending on the task, that can include:",[47,48,49,57,63,69],"ul",{},[50,51,52,56],"li",{},[53,54,55],"strong",{},"Your canvas state",": the nodes you've placed, how they connect, what they do.",[50,58,59,62],{},[53,60,61],{},"Your installed palette",": which nodes are installed and at what version.",[50,64,65,68],{},[53,66,67],{},"Your existing node configurations",": the settings already defined in the nodes on your canvas. You can also share context directly to let the Expert create the node configurations for external infrastructure, this is being improved into a more dedicated experience soon, and FlowFuse-native infrastructure will increasingly be recognised and considered automatically based on intent.",[50,70,71,74,75,79],{},[53,72,73],{},"Your runtime data",": when you attach it from the debug sidebar, Expert sees the ",[76,77,78],"code",{},"msg"," data, errors, and values flowing through each node. It reasons about runtime behavior, not just wiring. We're working on optimising and automating this soon.",[15,81,82],{},"The result is output you can trust to match what you expect, so you reach your intended outcome sooner.",[39,84,86],{"id":85},"get-started-in-two-minutes","Get started in two minutes",[15,88,89,90,94],{},"FlowFuse Expert is in open beta on FlowFuse Cloud (Team and Enterprise) — no request needed. It is also available on Self-Hosted Enterprise; ",[22,91,93],{"href":92},"\u002Fcontact-us\u002F?subject=FlowFuse%20Expert%20Application%20Building","contact us"," to get it set up. If you don't already have a FlowFuse account, [sign up]({% include \"sign-up-url.njk\" %}) first.",[15,96,97,98,102],{},"Once enabled, open the editor and ",[22,99,101],{"href":100},"\u002Fdocs\u002Fuser\u002Fexpert\u002Fchat\u002F#opening-the-chat-interface","find the FlowFuse Expert chat",". The chat is our integrated AI across the whole suite, so you'll find it everywhere from FlowFuse to Node-RED Editor.",[15,104,105],{},"Paste this in:",[15,107,108],{},[35,109,110],{},"\"Build a live dashboard monitoring three machines (Machine 1, Machine 2, Machine 3). Each one should keep switching on its own every 2 seconds between Running (green), Idle (yellow), or Fault (red), so at any moment they can be in different states, with the name, state, and colored indicator updating live on the page.\"",[15,112,113],{},"You can watch Expert do its work as the flow is built on the canvas. A few seconds later it's wired up and ready. Hit Deploy, open the dashboard, and the cards start cycling through Running, Idle, and Fault.",[15,115,116],{},"You didn't go hunting through the palette or keep the documentation open in another tab. The flow just showed up saving you time and effort.",[118,119,120],"blockquote",{},[15,121,122],{},"FlowFuse Expert works on both Hosted and Remote Instances.",[15,124,125],{},"From there, you can replace simulated data with live machine data, redesign the dashboard, add alarms, connect MQTT or OPC UA sources, and keep refining the application through prompts instead of manual building.",[127,128],"lite-youtube",{"videoid":129,"style":130,"title":131},"wzD02B7EaqM","width: 100%; aspect-ratio: 16\u002F9; background-image: url('\u002Fblog\u002F2026\u002F05\u002Fimages\u002Fflowfuse-expert-building-flow.jpg'); background-size: cover;","Building Industrial Apps With FlowFuse Expert",[39,133,135],{"id":134},"working-with-expert-day-to-day","Working with Expert day-to-day",[15,137,138],{},"Describe outcomes, not outputs. Tell Expert what you want to end up with and let it figure out the building blocks. Fine-tune the specifics afterward. And you're never locked in: you can keep working manually on the canvas right alongside Expert.",[15,140,141],{},"A few patterns that help you get the most out of Expert today. We're continuing to tune things so these matter less over time.",[143,144,146],"h3",{"id":145},"share-context-when-something-looks-off","Share context when something looks off",[15,148,149],{},"If a card's blank or a value's wrong, you don't have to hunt for the cause yourself. Expert can look up runtime data on its own. If you want to point it at something specific, you can also share context directly through the debug sidebar.",[143,151,153],{"id":152},"build-end-to-end-then-refine","Build end-to-end, then refine",[15,155,156],{},"Get the whole thing wired up first. Data flowing, dashboard bound, the basic loop running. Then iterate. Don't be afraid to dig deeper on any piece as you go.",[15,158,159],{},"If you need to fix specific things, it's best to do them one at a time for now, so you can verify each result.",[143,161,163],{"id":162},"deploy-with-your-context-in-mind","Deploy with your context in mind",[15,165,166],{},"If the flow is connected to a production process, take a minute to look over what Expert built before hitting Deploy. If it's a sandbox or a prototype, deploy away and see what happens. As Expert keeps improving, you'll be able to trust the output earlier without the manual check.",[143,168,170],{"id":169},"start-fresh-when-the-chat-gets-noisy","Start fresh when the chat gets noisy",[15,172,173],{},"AI models can get poisoned with unrelated context over time. If results start drifting, starting a new chat often gives better results than trying to dig out of a bad thread.",[39,175,177],{"id":176},"where-to-go-next","Where to go next",[15,179,180,181,185],{},"For the full reference on what FlowFuse Expert reads, how it interacts with your canvas, and advanced usage, see the ",[22,182,184],{"href":183},"\u002Fdocs\u002Fuser\u002Fexpert\u002F","FlowFuse Expert documentation",". We're updating these alongside the agentic experience as it evolves.",{"title":187,"searchDepth":188,"depth":188,"links":189},"",4,[190,192,193,200],{"id":41,"depth":191,"text":42},2,{"id":85,"depth":191,"text":86},{"id":134,"depth":191,"text":135,"children":194},[195,197,198,199],{"id":145,"depth":196,"text":146},3,{"id":152,"depth":196,"text":153},{"id":162,"depth":196,"text":163},{"id":169,"depth":196,"text":170},{"id":176,"depth":191,"text":177},{"type":202,"title":203,"description":204},"sign-up","Try FlowFuse Expert on your team","FlowFuse Expert Application Building is in open beta on FlowFuse Cloud, no request needed. Sign up and start building.","2026-05-13","FlowFuse Expert now builds applications from a description. Here's what that looks like, what Expert understands about your environment, and how to keep iterating.","md","\u002Fblog\u002F2026\u002F05\u002Fimages\u002Fflowfuse-ai-expert-tile.png","2026-06-04",{"keywords":211,"excerpt":212},"flowfuse ai expert, flowfuse expert, industrial automation, node-red, industrial dashboards, mqtt, opc ua, industrial iot, ai-assisted development, real-time monitoring, flowfuse cloud, machine monitoring",{"type":12,"value":213},[214],[15,215,17],{},true,"\u002Fblog\u002F2026\u002F05\u002Fflowfuse-expert-building-flows",{"title":8,"description":206},{"loc":217},"blog\u002F2026\u002F05\u002Fflowfuse-expert-building-flows","From a description to a running flow",[223,224],"flowfuse","ai","FlowFuse Expert can now build complete Node-RED applications from a natural language description, wiring nodes on the canvas in real time. Before building, Expert reads your installed palette, existing canvas state, node configurations, and runtime debug data to produce output that matches your actual environment. The feature is in open beta on FlowFuse Cloud (all tiers) and Self-Hosted Enterprise, and works on both Hosted and Remote Instances.","DR9OTIVtBLU","J0TH91iEfLewlnfKIa7OWB4nHoTOujBg7TTyRzn9-Jo",{"id":229,"title":230,"authors":231,"body":233,"cta":500,"date":503,"description":504,"extension":207,"image":505,"lastUpdated":3,"meta":506,"navigation":216,"path":523,"seo":524,"sitemap":525,"stem":526,"subtitle":527,"tags":528,"tldr":531,"video":532,"__hash__":533},"blog\u002Fblog\u002F2026\u002F04\u002Fflowfuse-release-2-29.md","FlowFuse 2.29: FlowFuse Expert Comes to Self-Hosted Enterprise",[232],"dimitrie-hoekstra",{"type":12,"value":234,"toc":483},[235,238,242,247,251,254,257,264,268,271,274,281,285,291,301,305,313,317,320,324,327,330,333,344,348,351,357,360,363,366,377,381,411,415,435,439,448,457,461,475],[15,236,237],{},"FlowFuse 2.29 gives teams more control over how flows move through their stack, makes it easier to understand what changed between versions, and brings FlowFuse Expert to self-hosted enterprise customers.",[39,239,241],{"id":240},"flowfuse-expert-available-to-more-teams-and-more-capable-expert","FlowFuse Expert, Available to More Teams and More Capable {#expert}",[15,243,244],{},[35,245,246],{},"FlowFuse Expert is our integrated AI assistant, one consistent surface across the FlowFuse website, platform, and immersive Node-RED editor for troubleshooting, building, and getting targeted help.",[143,248,250],{"id":249},"self-hosted-enterprise-expert-self-hosted","Self-Hosted Enterprise {#expert-self-hosted}",[15,252,253],{},"FlowFuse Expert was previously only available to cloud customers. Self-hosted enterprise teams had no equivalent surface for in-context troubleshooting and guidance.",[15,255,256],{},"Expert is now available for self-hosted enterprise FlowFuse instances. Your team gets the same contextual guidance and targeted help as cloud customers, with your operational data staying on your own infrastructure.",[15,258,259,263],{},[22,260,262],{"href":261},"\u002Fcontact-us\u002F?subject=FlowFuse%20Expert%20for%20Self-Hosted","Contact us"," to enable Expert on your self-hosted environment.",[143,265,267],{"id":266},"take-action-directly-from-expert-responses-expert-actions","Take Action Directly from Expert Responses {#expert-actions}",[15,269,270],{},"Expert responses previously surfaced information and suggestions. Acting on them, importing a flow, selecting relevant nodes, opening a new tab, required switching out of the conversation and doing it manually.",[15,272,273],{},"Expert responses can now include clickable action links. Click one and Expert performs the action directly in your editor: opening a new flow tab, selecting the nodes it just mentioned, or importing a flow from the conversation.",[15,275,276],{},[30,277],{"alt":278,"dataZoomable":187,"src":279,"style":280},"Expert action links demo","\u002Fblog\u002F2026\u002F04\u002Fsrc\u002Fblog\u002F2026\u002F04\u002Fimages\u002Fexpert-action-links.gif","border: 2px solid #E5E7EB;",[282,283,284],"figcaption",{},"Expert responses can now act on your behalf, click a link and Expert opens a tab, selects nodes, or imports a flow directly in your editor.",[15,286,287,290],{},[53,288,289],{},"Coming next:"," spinning up Node-RED instances directly from Expert, letting you go from idea to running flow without leaving the chat.",[292,293,296,297],"div",{"className":294},[295],"ff-related-changelogs","Changelog: ",[22,298,300],{"href":299},"https:\u002F\u002Fflowfuse.com\u002Fchangelog\u002F2026\u002F04\u002Fexpert-action-links\u002F","FlowFuse expert action links",[143,302,304],{"id":303},"in-practice","In practice",[47,306,307,310],{},[50,308,309],{},"You act on Expert suggestions in one click instead of manually applying them",[50,311,312],{},"You stay in the conversation while Expert works in your editor",[39,314,316],{"id":315},"more-visibility-and-control-across-your-deployment-workflow-deployment-workflow","More Visibility and Control Across Your Deployment Workflow {#deployment-workflow}",[15,318,319],{},"Managing flows across environments means tracking what changed, when, and by whom. When tooling gaps introduce friction here, or leave your version control workflow fragmented, they slow teams down at exactly the wrong moment.",[143,321,323],{"id":322},"azure-devops-git-integration-azure-devops","Azure DevOps Git Integration {#azure-devops}",[15,325,326],{},"FlowFuse's GitOps support previously required GitHub. Teams standardised on Azure DevOps had no native way to include Node-RED flows in their existing version control workflow.",[15,328,329],{},"FlowFuse 2.29 adds Azure DevOps as a supported Git provider. You can now push and pull snapshots directly from Azure DevOps repositories using Personal Access Tokens, configured under Team Settings → Integrations.",[143,331,304],{"id":332},"in-practice-1",[47,334,335,338,341],{},[50,336,337],{},"You connect Azure DevOps repos alongside or instead of GitHub",[50,339,340],{},"Your Node-RED flows participate in the same version control workflow as the rest of your stack",[50,342,343],{},"You authenticate with Azure Personal Access Tokens, with no secondary tooling required",[143,345,347],{"id":346},"see-exactly-what-changed-in-a-snapshot-snapshot-diff","See Exactly What Changed in a Snapshot {#snapshot-diff}",[15,349,350],{},"FlowFuse's snapshot comparison view showed flows side by side, but the visual alone doesn't always tell the whole story. You could see that a node was different, but not which specific property changed. When a function node's code changed, you couldn't identify which lines were different without manually diffing two code blocks outside of FlowFuse.",[15,352,353],{},[30,354],{"alt":355,"dataZoomable":187,"src":356,"style":280},"Snapshot diff demo","\u002Fblog\u002F2026\u002F04\u002Fsrc\u002Fblog\u002F2026\u002F04\u002Fimages\u002Fsnapshot-comparision-view-2.29.png",[282,358,359],{},"The compare dialog now shows exactly which properties changed and highlights line-level differences in function code, templates, and JSON, no manual diffing required.",[15,361,362],{},"The compare dialog now includes a property-level diff sidebar: structural property changes old to new at a glance, and git-style line diffs for function code, template HTML, and JSON. A navigation bar steps through every changed, added, or deleted node with arrow key shortcuts. The canvas highlights and scrolls to the current node as you navigate.",[143,364,304],{"id":365},"in-practice-2",[47,367,368,371,374],{},[50,369,370],{},"You review what changed between dev and production without leaving FlowFuse",[50,372,373],{},"You validate a teammate's update at the property level, not just the node level",[50,375,376],{},"You debug why a flow changed after a deploy with the same tooling you use to promote it",[39,378,380],{"id":379},"what-else-is-new","What else is new?",[47,382,383,389,395,405],{},[50,384,385,388],{},[53,386,387],{},"Expert opens by default",": FlowFuse Expert now opens automatically when you visit the editor for the first time. If you close it, that preference is remembered across browser sessions.",[50,390,391,394],{},[53,392,393],{},"Embedded editor tab titles",": Hosted and Remote Instance editor tabs now show the actual Node-RED flow name rather than a generic title.",[50,396,397,400,401,404],{},[53,398,399],{},"Instance URL env var",": Hosted Node-RED instances now expose an ",[76,402,403],{},"FF_INSTANCE_URL"," environment variable containing the instance's URL (default or custom hostname). Useful for flows that need to know their own address, like webhook callbacks or OAuth redirects.",[50,406,407,410],{},[53,408,409],{},"Blueprint markdown rendering",": Blueprint descriptions now support markdown rendering, so formatting like headers and lists display as intended.",[143,412,414],{"id":413},"fixes","Fixes",[47,416,417,423,429],{},[50,418,419,422],{},[53,420,421],{},"MCP server discoverability",": Older MCP servers that were registered on your instances were not showing up in Expert Insights mode. All registered MCP servers are now discoverable again.",[50,424,425,428],{},[53,426,427],{},"Snapshot detail in the immersive editor",": Reviewing a snapshot from inside the immersive editor now opens it in a modal, so you can inspect snapshots without leaving the editor.",[50,430,431,434],{},[53,432,433],{},"Developer Mode tab restored in the immersive editor",": The Developer Mode tab is back in the immersive editor drawer, letting you toggle Auto Snapshots and create snapshots without opening a second window.",[143,436,438],{"id":437},"node-red","Node-RED",[15,440,441,447],{},[22,442,446],{"href":443,"rel":444},"https:\u002F\u002Fgithub.com\u002Fnode-red\u002Fnode-red\u002Freleases\u002Ftag\u002F4.1.8",[445],"nofollow","Node-RED 4.1.8"," is now available as a stack option in FlowFuse. Highlights include function node tab badges (see at a glance which tabs contain code), theme plugin overrides for settings and menu options, configurable palette categories via theme plugins, and show-first\u002Flast-tab keyboard actions.",[15,449,450,451,456],{},"Looking ahead, ",[22,452,455],{"href":453,"rel":454},"https:\u002F\u002Fnodered.org\u002Fblog\u002F2025\u002F12\u002F03\u002Fnode-red-roadmap-to-5",[445],"Node-RED 5.0"," is in beta. It's a modernization and UI re-architecture that readies Node-RED for better AI-guided development and brings more clarity to manual editing. FlowFuse will ship 5.0 once it reaches stable release.",[458,459],"hr",{"style":460},"margin: 3rem 0; border: 0; border-top: 1px solid #D1D5DB;",[15,462,463,464,468,469,474],{},"For detailed breakdowns of each feature with additional visuals, visit our ",[22,465,467],{"href":466},"\u002Fchangelog\u002F","changelog",". For the complete list of everything included in FlowFuse 2.29, check out the ",[22,470,473],{"href":471,"rel":472},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases",[445],"release notes",".",[15,476,477,478,482],{},"If something in this release improves your workflow, or if there is still friction we can remove, please ",[22,479,481],{"href":480},"mailto:contact@flowfuse.com?subject=Feedback%20on%202.29","share feedback or report issues regarding this release"," to us.",{"title":187,"searchDepth":188,"depth":188,"links":484},[485,490,496],{"id":240,"depth":191,"text":241,"children":486},[487,488,489],{"id":249,"depth":196,"text":250},{"id":266,"depth":196,"text":267},{"id":303,"depth":196,"text":304},{"id":315,"depth":191,"text":316,"children":491},[492,493,494,495],{"id":322,"depth":196,"text":323},{"id":332,"depth":196,"text":304},{"id":346,"depth":196,"text":347},{"id":365,"depth":196,"text":304},{"id":379,"depth":191,"text":380,"children":497},[498,499],{"id":413,"depth":196,"text":414},{"id":437,"depth":196,"text":438},{"type":202,"title":501,"description":502},"Try the latest FlowFuse improvements in your own environment","Use Expert to take action in your editor, connect Azure DevOps to your workflow, and see exactly what changed between snapshots.","2026-04-09","FlowFuse 2.29 brings FlowFuse Expert to self-hosted enterprise customers, adds Azure DevOps as a supported Git provider, and makes snapshot comparisons clearer with property-level diffs.","\u002Fblog\u002F2026\u002F04\u002Fimages\u002Fflowfuse-release-2-29.png",{"release":507,"features":508,"excerpt":519},"2.29",[509,512,515,518],{"id":510,"heading":511},"git-integration-azure","Azure DevOps Git Integration",{"id":513,"heading":514},"snapshot-compare","See Exactly What Changed in a Snapshot",{"id":516,"heading":517},"ff-expert","FlowFuse Expert, Available to More Teams and More Capable",{"heading":380},{"type":12,"value":520},[521],[15,522,237],{},"\u002Fblog\u002F2026\u002F04\u002Fflowfuse-release-2-29",{"title":230,"description":504},{"loc":523},"blog\u002F2026\u002F04\u002Fflowfuse-release-2-29","Self-Hosted Enterprise customers can now enable FlowFuse Expert. Plus Azure DevOps Git support and clearer snapshot comparisons.",[223,529,530,224],"news","releases","FlowFuse 2.29 brings three main updates: FlowFuse Expert is now available for self-hosted enterprise customers and can execute actions directly in the Node-RED editor; Azure DevOps is now a supported Git provider for GitOps workflows; and snapshot comparisons now include a property-level diff sidebar with git-style line diffs. The release also ships Node-RED 4.1.8 as a stack option.","lz4bu7d1AH0","DvME8aavEEWOVR2YrT37sxmL-UjsPXnveEQjEDih1GQ",{"id":535,"title":536,"authors":537,"body":539,"cta":602,"date":605,"description":604,"extension":207,"image":606,"lastUpdated":3,"meta":607,"navigation":216,"path":609,"seo":610,"sitemap":611,"stem":612,"subtitle":613,"tags":614,"tldr":3,"video":3,"__hash__":615},"blog\u002Fblog\u002F2026\u002F03\u002Frethinking-edge-ais-core-orchestration.md","Rethinking Edge AI's Core Orchestration",[538],"zeger-jan-van-de-weg",{"type":12,"value":540,"toc":595},[541,544,548,551,555,558,562,565,569,577,580,588,592],[15,542,543],{},"Every week, another vendor promises \"AI on the edge\" with glossy demos and yet another dashboard. With FlowFuse customers though, in real factories, the hard part is not the model; it is getting trustworthy, contextual data from all machines to the right AI, at the right time.The real competitive advantage will not come from who has the flashiest model, but from who owns the most reliable connecting tissue between hardware, IT systems, AI agents, and the cloud. This tissue empowers better model accuracy and creates an operational advantage for our customers.",[39,545,547],{"id":546},"why-ai-deployments-fail-when-you-forget-about-connectivity","Why AI Deployments Fail When You Forget About Connectivity",[15,549,550],{},"Manufacturers that have already modernized their SCADA or rolled out cloud data lakes, lake houses, and other lake-based properties, are often surprised by how fragile their \"AI initiative\" becomes once it touches the factory floor.\nIt is no secret that data is trapped in proprietary PLCs and vendor‑locked SCADA systems, with IT and OT speaking different languages or protocols, and every site maintaining its own patchwork of scripts, gateways, and one‑off integrations.\nSo what happens? You end up with predictable problems: fragile, one-off connections, IT changes that take half a year, OT changes that have impact on data semantics but go unnoticed and AI projects that look great on paper but never actually run in the real world at scale.\nThe fundamental issue lies in the deep divide between Information Technology (IT) and Operational Technology (OT). Shop floor teams are often left in the dark about how their data is leveraged by upstream systems, while IT teams struggle to grasp the operational significance underlying the tags and signals they process. This unbridged gap ensures that every AI initiative remains an expensive, custom-built, and inherently brittle undertaking.",[39,552,554],{"id":553},"the-new-stack-aidriven-edge-connectivity","The new stack: AI‑driven edge connectivity",[15,556,557],{},"A different architecture is emerging: a unified, future-proof connectivity layer that sits between machines, plant networks, enterprise systems, and AI services. This layer becomes the common ground where IT and OT share a unified model of metrics from assets, other events, and decisions made, ensuring that neither side operates in a vacuum. And I have to say that I’m proud of stating that FlowFuse is at the forefront of this innovation.\nIn this new paradigm we’re facing, edge devices run a consistent runtime, such as open-source Node‑RED, so that connectivity, transformation, and control logic look the same on every line, every site, and every device family.\nAI is then wired directly into this connective tissue via emerging AI standards like Model Context Protocol (MCP) and other native AI nodes, turning the connectivity fabric into a living \"nerve system\" where agents can safely read, reason, and act on live operational data.",[39,559,561],{"id":560},"ok-but-what-future-proof-means-at-the-edge","OK but… what \"Future-Proof\" means at the edge?",[15,563,564],{},"Getting AI, especially large language models (LLMs), to work with the edge isn't just about sticking them onto your existing middleware. It means that AI is embedded into how connectivity is designed, deployed, and operated: models and agents that turn natural‑language descriptions into flows, blueprints that spin up AI chat agents over plant data, and smart suggestions that propose the next node or transformation in context.\nThe main thing is, it also means that AI agents can run close to the process, on a gateway or industrial PC, using ONNX models for things like spotting defective parts produced,  and predicting maintenance and downtime, while the cloud handles the rules, oversight, and updates for the whole fleet.\nThat split (smart procedures happening at the edge, and the cloud just keeping things organized) is precisely what makes the IT and OT worlds finally click. OT gets to be fast and independent, and IT still has the main view and control over the whole shebang.",[39,566,568],{"id":567},"we-can-finally-say-goodbye-to-the-itot-gap","We can finally say goodbye to the IT\u002FOT gap.",[15,570,571,572,576],{},"FlowFuse was built on a simple belief: industrial teams need one platform that can connect any machine, move data across any protocol, model it in any data platform, and run applications wherever they create the most value.\nBoth IT and OT get the tools they need, from a wide connectivity suite, to Git integration, and a platform to scale it to hundreds or thousands of different devices.It hooks up to things like PLCs, sensors, and existing Operational Technology (OT) gear using standard protocols like Modbus, OPC UA, and MQTT. This lets you securely stream data to the cloud or your own premises without messing around with complicated firewalls or VPNs. You name it, FlowFuse will connect everything.\nAt the enterprise level, FlowFuse Cloud provides the central \"control tower\" to standardize blueprints, ensure version consistency, enforce security policies, and roll out changes across hundreds or thousands of devices with a single action. For an ",[22,573,575],{"href":574},"\u002Findustries\u002Fautomotive\u002F","automotive"," and and other manufacturers, that's what keeps AI initiatives across multiple teams standardized and governed instead of turning into a collection of disconnected, one-off solutions.",[15,578,579],{},"On top of this connectivity fabric, FlowFuse Expert acts as the AI assistant tuned for industrial teams that will make your life easier as you never imagined.\nGrounded via MCP in your actual machines, brokers, and databases, it does much more than chat: it generates live Node‑RED flows, data mappings, dashboards, and even queries that are directly deployable into your environment. Because it connects to both OT sources (PLCs, industrial brokers, historians) and IT systems (data lakes, CMDBs, corporate apps), Expert sees, understands and manages both halves of the map. 100% control of both IT and OT. It translates plant-floor requirements into IT-compliant flows and ensures that security and governance policies are automatically applied to edge configurations. It acts as the structural bridge that finally closes the IT\u002FOT gap.\nFor OT engineers, this means months‑long projects compress into days or minutes; for IT, it means governance and security controls stay intact even as more of the work shifts closer to the plant floor.",[15,581,582,586],{},[30,583],{"alt":584,"src":585},"IT\u002FOT Gap Diagram","\u002Fblog\u002F2026\u002F03\u002Fimages\u002Frethinking-edge-ais-core-orchestration-diagram.png",[35,587,584],{},[39,589,591],{"id":590},"from-pilots-to-a-scalable-ai-nerve-system","From pilots to a scalable \"AI nerve system\"",[15,593,594],{},"Here’s the thing - Most companies will not win by building the most sophisticated individual model, but by institutionalizing a repeatable pattern: connect, contextualize, and act on data at the edge, with AI embedded at every step. If you’ve followed FlowFuse’s history you’ve probably seen how our tagline and our storytelling has been evolving following that logic. “Connect, Collect, Build and Scale with FlowFuse” - This is what this is about.\nFlowFuse gives manufacturers that pattern in a form they can actually operate: open‑source at the core with Node‑RED, industrial‑grade features for deployment and observability, and a layer of proprietary Artificial Intelligence that actually understands the realities of plant networks, not just cloud APIs.\nThe result is a new kind of infrastructure: a persistent \"AI nerve system\" that spans hardware, IT, AI, and cloud, so that when the next model, vendor, or use case arrives, your connectivity is already in place, deployed and ready to scale. This structural bridge ensures that the historical friction between IT and OT becomes a foundation for collaboration.\nIf the last decade was about moving workloads to the cloud, the next decade in manufacturing will be about bringing intelligence to the edge. And how this must be done? Safely, consistently, and fast enough to matter.\nThe companies that treat AI‑driven edge connectivity as a strategic foundation, not a side project, will be the ones that turn experiments into durable competitive advantage.",{"title":187,"searchDepth":188,"depth":188,"links":596},[597,598,599,600,601],{"id":546,"depth":191,"text":547},{"id":553,"depth":191,"text":554},{"id":560,"depth":191,"text":561},{"id":567,"depth":191,"text":568},{"id":590,"depth":191,"text":591},{"type":603,"title":536,"description":604},"demo","How AI-driven edge connectivity is redefining industrial operations, bridging IT and OT, and turning AI pilots into scalable, real-world impact.","2026-03-27","\u002Fblog\u002F2026\u002F03\u002Fimages\u002Frethinking-edge-ais-core-orchestration.png",{"keywords":608},"Edge AI, Node-RED, FlowFuse, industrial automation, OT IT integration, knowledge management, IIoT integration, PLC integration, industrial AI, Edge connectivity","\u002Fblog\u002F2026\u002F03\u002Frethinking-edge-ais-core-orchestration",{"title":536,"description":604},{"loc":609},"blog\u002F2026\u002F03\u002Frethinking-edge-ais-core-orchestration","2026 is well underway - where are you going?",[223,224],"SOlztFLNOOghT24-S8_yFykWCyLGuVQgOm9ZAadp0C0",{"id":617,"title":618,"authors":619,"body":620,"cta":798,"date":801,"description":802,"extension":207,"image":803,"lastUpdated":3,"meta":804,"navigation":216,"path":810,"seo":811,"sitemap":812,"stem":813,"subtitle":814,"tags":815,"tldr":3,"video":3,"__hash__":816},"blog\u002Fblog\u002F2026\u002F03\u002Fai-usecases-in-factory.md","5 Places Smart Factories Are Already Using AI",[10],{"type":12,"value":621,"toc":790},[622,625,628,631,634,637,640,644,647,650,653,661,664,668,671,674,694,697,700,704,707,710,716,720,723,726,741,749,753,756,759,768,771,774,778,781,784,787],[15,623,624],{},"The factory floor wasn't exactly an early adopter of artificial intelligence.",[15,626,627],{},"It's a world built around physical processes: tolerances, throughput, shift schedules. The automation that arrived decades ago was powerful but rigid. Machines that did exactly what you programmed them to do, nothing more.",[15,629,630],{},"That's changed.",[15,632,633],{},"AI is now embedded in manufacturing operations in ways that are easy to miss. Not in the headline-grabbing robots, but in the systems quietly running underneath: predicting failures, catching defects, optimizing energy loads, and compressing deployment timelines.",[15,635,636],{},"The question isn't whether AI has reached the factory floor. It has. The question is where it's actually doing useful work.",[15,638,639],{},"Here are five answers.",[39,641,643],{"id":642},"predictive-maintenance-on-cnc-machines","Predictive Maintenance on CNC Machines",[15,645,646],{},"Manufacturers lose $50 billion a year to unplanned downtime. Here's what that actually looks like: a CNC machine goes down at 2am, the part isn't in stock, the order misses its window, and the schedule takes three days to recover.",[15,648,649],{},"The problem was never detection. Factories have had sensors for decades. It was interpretation. A temperature spike means nothing without knowing what's normal for that machine, on that material, at that feed rate. Threshold-based alarms can't know that. A model trained on months of operational data from that specific machine can.",[15,651,652],{},"Predictive maintenance learns the baseline and watches for drift. Bearing wear shows up as a frequency shift in vibration data. Spindle imbalance leaves a signature in motor current. None of these are visible on the floor, but all of them are readable in the data, 24 to 72 hours before failure.",[15,654,655,656,660],{},"Harley-Davidson has been running vibration-based predictive maintenance on plant equipment for longer than most manufacturers realize. But you don't need that scale. We built an ",[22,657,659],{"href":658},"\u002Fblog\u002F2026\u002F02\u002Fmotor-anomaly-detector-ai\u002F","AI vibration anomaly detector for industrial motors"," using an autoencoder trained on healthy vibration data, running inference directly in Node-RED. The model learns what normal looks like. When that changes, you get a warning with time to act.",[15,662,663],{},"That's the shift. Not better alarms, but a system that knows the difference between a machine running hard and a machine running out of time.",[39,665,667],{"id":666},"visual-quality-inspection","Visual Quality Inspection",[15,669,670],{},"Manual inspection has a hard ceiling. Put someone at a line running 200 units a minute for four hours and their defect detection rate drops. That's not a training problem. It's physiology.",[15,672,673],{},"Computer vision doesn't have that ceiling. A camera at the inspection station sees every unit, not a sample, at full line speed, catching surface defects, dimensional drift, solder bridges, and weld inconsistencies. In semiconductor fabrication, where a particle smaller than a human hair destroys yield, vision-based inspection isn't an upgrade. It's the only option that makes sense at volume.",[15,675,676,677,682,683,688,689,693],{},"BMW's ",[22,678,681],{"href":679,"rel":680},"https:\u002F\u002Fwww.press.bmwgroup.com\u002Fglobal\u002Farticle\u002Fdetail\u002FT0449729EN\u002Fartificial-intelligence-as-a-quality-booster?language=en",[445],"AIQX platform"," runs camera and sensor-based AI quality checks across every plant globally. At Spartanburg, it monitors roughly ",[22,684,687],{"href":685,"rel":686},"https:\u002F\u002Fwww.automotivemanufacturingsolutions.com\u002Fsmart-factory\u002Fquality-visions-ai-inspection-systems-that-learns-from-the-line\u002F2623126",[445],"half a million weld studs daily",". At Regensburg, the ",[22,690,692],{"href":679,"rel":691},[445],"GenAI4Q system"," generates a custom inspection checklist for each of 1,400 vehicles built daily, adapting to every model variant in real time.",[15,695,696],{},"There's a compounding effect worth noting: every defect caught becomes labeled training data. The model improves continuously. The longer it runs, the harder it is to beat.",[15,698,699],{},"Human inspectors still matter for root cause and edge cases. But the first pass, the one that runs on every unit at line speed, is not a job for human eyes anymore.",[39,701,703],{"id":702},"shorter-path-from-pilot-to-production","Shorter Path from Pilot to Production",[15,705,706],{},"Manufacturers consistently underestimate integration time. The technology decisions get made, the use case is clear, and then the project stalls for months. The pilot ran on clean exported data. Production means live data from PLCs running proprietary protocols, historians not designed for real-time access, and network segments that exist for good reasons. Most IIoT initiatives don't fail at the algorithm layer. They fail at the plumbing.",[15,708,709],{},"That gap is expensive. A deployment that takes six months to reach production has a very different ROI profile than one that takes six weeks. And it's precisely here that AI is changing the economics of development. Engineers are generating integration flows from plain language descriptions, tracing protocol mismatches that would have previously burned days, and producing documentation as they build rather than long after. The unglamorous middle work moves faster.",[15,711,712,715],{},[22,713,714],{"href":183},"FlowFuse Expert"," is built for exactly this. AI assistance embedded directly in the Node-RED editor: inline code completions as you write, next-node predictions as you build, and a chat interface that understands your actual flows and live debug output. Describe the logic you need and it writes the function node. Something behaving unexpectedly? Load your flow and debug logs as context and work through it together. Fewer hours lost to problems that have already been solved a hundred times before.",[39,717,719],{"id":718},"energy-and-hvac-optimization","Energy and HVAC Optimization",[15,721,722],{},"Most smart factory conversations never get to the building itself. That's a mistake.",[15,724,725],{},"HVAC is one of the largest energy costs in a manufacturing facility and one of the least optimized. A factory's thermal load shifts constantly: which lines are running, what materials are being processed, outdoor temperature, occupancy. Rule-based systems deal with that complexity by setting conservative margins everywhere and leaving them there. It works, but you pay for it on every energy bill.",[15,727,728,729,734,735,740],{},"AI learns the actual behavior of the facility and stops defending against conditions that aren't happening. Yokogawa deployed reinforcement learning for HVAC in its semiconductor plant in Japan. In that facility, cleanroom HVAC accounted for roughly ",[22,730,733],{"href":731,"rel":732},"https:\u002F\u002Fwww.isa.org\u002Fintech-home\u002F2022\u002Foctober-2022\u002Ffeatures\u002Fcase-study-ai-based-autonomous-control",[445],"30 percent of total energy consumption",", a figure consistent with, though on the lower end of, what's reported across the semiconductor industry. The result was a ",[22,736,739],{"href":737,"rel":738},"https:\u002F\u002Fmy.avnet.com\u002Fsilica\u002Fresources\u002Farticle\u002Fai-takes-on-growing-role-in-hvac-system-efficiencies\u002F",[445],"3.6 percent reduction"," in total consumption. Modest on paper, significant at scale, and it improves as the model keeps learning.",[15,742,743,748],{},[22,744,747],{"href":745,"rel":746},"https:\u002F\u002Fdeepmind.google\u002Fdiscover\u002Fblog\u002Fdeepmind-ai-reduces-google-data-centre-cooling-bill-by-40\u002F",[445],"DeepMind's data center work"," gets more attention, but that's a controlled, single-purpose environment. A factory is harder. Yokogawa is the more relevant proof of concept.",[39,750,752],{"id":751},"worker-safety-and-ergonomics-monitoring","Worker Safety and Ergonomics Monitoring",[15,754,755],{},"Musculoskeletal disorders are the most common workplace injury in manufacturing. A bad lift, an awkward reach, a sustained bent posture on a repetitive task: the injury doesn't happen dramatically. It accumulates over weeks, then shows up as a compensation claim and a gap on the line.",[15,757,758],{},"Traditional ergonomics assessment catches this late. A consultant observes a workstation, writes a report, and by the time recommendations are implemented, workers have been loading their joints wrong for months.",[15,760,761,762,767],{},"Pose-estimation AI runs overhead cameras continuously and scores every movement in real time against established ergonomic risk frameworks, flagging a bend angle that will cause a back injury long before it materializes. In one ",[22,763,766],{"href":764,"rel":765},"https:\u002F\u002Frsisinternational.org\u002Fjournals\u002Fijrsi\u002Farticles\u002Fai-powered-ergonomics-enhancing-workplace-safety-through-posture-detection\u002F",[445],"manufacturing pilot study",", researchers reported roughly a 25 percent reduction in workplace injuries, with posture compliance improving once workers received real-time feedback. Results will vary by facility, workstation type, and baseline injury rates.",[15,769,770],{},"The privacy question deserves a direct answer: these systems track skeletal keypoints, not faces or identities. What gets logged is joint angle data, not footage of individuals.",[15,772,773],{},"What makes this different from a safety poster is that it doesn't rely on the worker remembering. The system watches every repetition, on every shift.",[39,775,777],{"id":776},"where-to-start","Where to Start",[15,779,780],{},"The five use cases covered here aren't experiments. They're running in production facilities today, on real lines, delivering measurable results. The gap between manufacturers who've deployed AI and those still evaluating it is growing, and it compounds.",[15,782,783],{},"But starting doesn't require a complete digital transformation. Pick one problem that's costing you money right now: unplanned downtime on a critical machine, a defect rate you can't get below, an energy bill that doesn't reflect how efficiently you actually run. That's your first use case.",[15,785,786],{},"The common thread across all five use cases is the same bottleneck: getting operational data to a model reliably and acting on what it returns. That's the integration problem most deployments stall on. FlowFuse is built around solving exactly that. You connect to your PLCs, deploy your AI model, and trigger actions all within the same flow, which is why it's where most manufacturers start and where they keep building.",[15,788,789],{},"The manufacturers seeing results didn't wait for the perfect conditions. They started with one line, proved the value, and expanded. That's still the fastest route from where you are to where you want to be.",{"title":187,"searchDepth":188,"depth":188,"links":791},[792,793,794,795,796,797],{"id":642,"depth":191,"text":643},{"id":666,"depth":191,"text":667},{"id":702,"depth":191,"text":703},{"id":718,"depth":191,"text":719},{"id":751,"depth":191,"text":752},{"id":776,"depth":191,"text":777},{"title":799,"description":800},"Start With One Use Case. Build From There.","Whether it's predictive maintenance, quality inspection, or energy optimization, FlowFuse connects your OT data to AI models without a multi-year transformation.","2026-03-24","Most manufacturers are still debating AI adoption. These five use cases are already running in production, cutting downtime, scrap, energy costs, and injury rates.","\u002Fblog\u002F2026\u002F03\u002Fimages\u002Fai-use-case.png",{"keywords":805,"excerpt":806},"AI in manufacturing, smart factory AI, predictive maintenance CNC machines, visual quality inspection AI, computer vision manufacturing, IIoT AI use cases, factory floor AI, HVAC energy optimization manufacturing, AI worker safety ergonomics, manufacturing AI examples, Node-RED IIoT integration, unplanned downtime manufacturing, AI anomaly detection industrial motors, Industry 4.0 AI",{"type":12,"value":807},[808],[15,809,624],{},"\u002Fblog\u002F2026\u002F03\u002Fai-usecases-in-factory",{"title":618,"description":802},{"loc":810},"blog\u002F2026\u002F03\u002Fai-usecases-in-factory","Where AI Is Actually Working on the Factory Floor",[223,224],"tpELGY7XvXqpMDepaLyy2bNTM3w7cEWaLKCrAReAzIw",{"id":818,"title":819,"authors":820,"body":821,"cta":973,"date":977,"description":978,"extension":207,"image":979,"lastUpdated":3,"meta":980,"navigation":216,"path":987,"seo":988,"sitemap":989,"stem":990,"subtitle":991,"tags":992,"tldr":3,"video":3,"__hash__":993},"blog\u002Fblog\u002F2026\u002F03\u002Flast-mile-problem-ai.md","The Last Mile Problem in Industrial AI",[10],{"type":12,"value":822,"toc":966},[823,826,829,832,835,838,841,844,848,851,854,857,860,863,866,869,872,875,879,882,885,888,891,894,898,901,904,907,910,913,916,919,922,925,929,932,935,938,941,944,947,950,954,957,960,963],[15,824,825],{},"There is a slide that lives in almost every industrial AI project deck.",[15,827,828],{},"On the left: data collection, model training, validation. On the right: operational value, reduced downtime, optimized throughput. In the middle, a small box labeled \"deployment\" that nobody in the room questions, because everyone has already moved on to the numbers on the right.",[15,830,831],{},"That box is where most industrial AI projects end.",[15,833,834],{},"Not with a failure report. Not with a cancelled contract. They end slowly, in staging environments that quietly become permanent, in quarterly reviews where \"ongoing\" gets said for the fourth time with less conviction than the third. The model is fine. The model has always been fine. The floor looks exactly the same as it did before the project started.",[15,836,837],{},"What lives inside that box is harder than the model and less interesting to talk about. It is the work of connecting intelligent software to a plant that was not built to receive it. Machines that predate wireless. Protocols designed for reliability, not interoperability. Engineers who know every quirk of every line and have watched enough consultants walk through with laptops to reserve judgment until something actually works.",[15,839,840],{},"In logistics, the last mile is the final stretch of a delivery, the leg that accounts for more than half the cost of shipping and has defeated every attempt to engineer it away. Industrial AI has the same problem. Not measured in distance. Measured in the gap between a model that performs and a plant that benefits.",[15,842,843],{},"Most industrial AI projects are funded to build the model. The mile after it gets a box on a slide.",[39,845,847],{"id":846},"the-scoping-lie","The Scoping Lie",[15,849,850],{},"Every industrial AI project proposal looks roughly the same.",[15,852,853],{},"There is a discovery phase. A data assessment. A model development stage with clear milestones and measurable accuracy targets. And then, near the bottom of the document, a deployment section that is usually one page, sometimes half a page, occasionally a single bullet point that says something like \"integration with existing systems\" without specifying which systems, how long that will take, or who is responsible for it.",[15,855,856],{},"That bullet point is where the project dies. It just takes six months to find out.",[15,858,859],{},"The people writing these proposals are not being dishonest. They are being optimistic in the way that every vendor is optimistic when the contract has not been signed yet. The integration work is real, they know it is real, but it is also the part of the project that is hardest to scope without knowing the plant, the protocols, the historian configuration, the network topology, the IT security policies, and a dozen other variables that only become visible once someone is standing on the floor with access credentials and a growing sense of unease.",[15,861,862],{},"So it gets compressed. One line. One assumption. One box on a slide.",[15,864,865],{},"And the buyer approves it, because the buyer is looking at the numbers on the right side of the timeline, the ones showing reduced downtime and optimized throughput, and the deployment box is between them and those numbers, and it looks small.",[15,867,868],{},"It is not small.",[15,870,871],{},"The integration work in a real industrial environment is not a technical footnote. It is the project. Connecting an AI model to a plant means touching systems that the OT team has kept stable for a decade and does not want anyone near. It means translating between protocols that were never designed to talk to each other. It means getting IT and OT into the same room, agreeing on data ownership, network access, security boundaries, and update procedures for infrastructure that both teams think belongs to the other.",[15,873,874],{},"None of that is in the proposal. All of it determines whether the proposal was worth signing.",[39,876,878],{"id":877},"the-cost-of-standing-still","The Cost of Standing Still",[15,880,881],{},"Pilot purgatory feels like a neutral state. The project is not cancelled. Progress is being made. The model is ready whenever the integration catches up.",[15,883,884],{},"It is not neutral.",[15,886,887],{},"Every month an AI model sits in staging is a month of decisions made on instinct instead of intelligence. A predictive maintenance model that never reached the floor did not just fail to deliver value. It failed to prevent every unplanned downtime event it would have caught. Every quality escape the anomaly detector would have flagged. Every maintenance window scheduled too late or too early because the optimization model was still \"ongoing.\"",[15,889,890],{},"That cost does not appear in any project report. It is invisible precisely because the thing that would have measured it never got deployed. But it is real, and it compounds.",[15,892,893],{},"Every stalled initiative also makes the next one harder to fund. The VP who approved the last pilot is not writing another check with the same enthusiasm. The organizational appetite for transformation is not infinite. Stalled pilots consume it without producing anything in return.",[39,895,897],{"id":896},"the-ownership-vacuum","The Ownership Vacuum",[15,899,900],{},"Here is a question worth asking before the next industrial AI project gets funded.",[15,902,903],{},"When the model is in production and something goes wrong at 2 a.m. on a Saturday, who gets the call?",[15,905,906],{},"In most manufacturing organizations, nobody has a clean answer to that. The data science team built the model but does not own the plant systems it connects to. The OT team owns the plant but did not build the model and does not have visibility into why it is behaving the way it is. IT owns the network the data travels across but considers the edge devices an OT problem. Nobody owns the pipeline between them.",[15,908,909],{},"This is not a technology problem. It is a leadership problem that has been dressed up as one.",[15,911,912],{},"The technology gap between a trained model and a production deployment is real, but it is solvable. Talented engineers solve harder problems every day. What stops them is not the complexity of the integration. It is the absence of anyone whose job it is to own the outcome. When the integration work falls between two teams and neither team has it in their objectives, it does not get done. It gets discussed. It gets escalated. It gets added to the agenda of a cross-functional meeting that gets rescheduled twice and then produces a decision to form a working group.",[15,914,915],{},"Meanwhile the model sits in staging.",[15,917,918],{},"The IT\u002FOT divide gets talked about as a technical challenge, a matter of protocols and network segmentation and data formats. Those things are real. But the deeper divide is organizational. Two teams, built for different purposes, measured on different outcomes, reporting to different leaders, looking at the same plant and seeing completely different problems. IT sees a security perimeter to protect. OT sees uptime to defend. Neither is wrong. Neither is looking at the gap between them.",[15,920,921],{},"That gap does not close itself. It closes when someone in the organization is explicitly responsible for closing it, with the authority to make decisions across both teams and the mandate to finish what the project started.",[15,923,924],{},"Most industrial AI initiatives are not structured that way. The project has a data science lead and a project manager and a steering committee. It does not have an integration owner. And so the last mile, the mile that requires both teams to move toward each other, stays exactly as wide as it was on day one.",[39,926,928],{"id":927},"what-closing-the-last-mile-actually-requires","What Closing the Last Mile Actually Requires",[15,930,931],{},"The answer is not a better model. It is not a bigger data science team. It is not another vendor promising that this time the integration will be straightforward.",[15,933,934],{},"It is infrastructure. Built deliberately, before the model needs it, designed for the environment it will actually run in.",[15,936,937],{},"That means starting with connectivity that meets the plant where it is, not where the vendor deck imagines it to be. Real plants run Modbus, OPC-UA, Siemens S7, proprietary historian formats, and protocols that were old before most current software engineers started their careers. The integration layer has to speak all of it, fluently, without requiring the OT team to replace equipment that is working perfectly and will continue working perfectly for another decade.",[15,939,940],{},"It means edge execution that does not depend on the cloud. A prediction that requires a round trip to a cloud inference endpoint is a prediction that fails the moment the network hiccups, which in a manufacturing environment is not a rare event. Intelligence needs to run close to the equipment it is monitoring, locally, with enough resilience to keep functioning when connectivity is degraded and enough security to satisfy the IT team that approved it onto the network.",[15,942,943],{},"It means deployment infrastructure that OT teams can actually own. The data scientist who trained the model will not be available at 2 a.m. on a Saturday. The update that fixes the drift in the anomaly detector cannot wait for a change request to clear a two-week approval queue. The people running the plant need to be able to deploy, update, monitor, and roll back AI systems through tooling that respects their domain knowledge without demanding software development skills they were never hired to have.",[15,945,946],{},"And it means governance that scales across facilities from the beginning. One plant is a pilot. Twelve plants is a program. The infrastructure that works for one site needs to work for all of them, with consistent versioning, auditable change history, role-based access, and the ability to push a validated update across a fleet without touching each device individually. Organizations that build for one site and retrofit for scale spend years rebuilding what they should have built once.",[15,948,949],{},"This is what the proposal compressed into a single box on a slide. Not one problem. Four interconnected ones, each of which can stop a deployment on its own, all of which need to be solved together before the model delivers anything.",[39,951,953],{"id":952},"the-mile-is-closable","The Mile Is Closable",[15,955,956],{},"The last mile problem in industrial AI is not a technology problem waiting for a breakthrough. The technology exists. The manufacturers still stuck in pilot purgatory cannot blame the tools.",[15,958,959],{},"It is a prioritization problem. A scoping problem. A decision, made early in every project, about what the work actually is and what it will take to finish it.",[15,961,962],{},"The manufacturers generating real operational value from AI today made a different decision. They treated integration as the foundation, not the footnote. They gave the last mile the budget it deserved, the ownership it required, and the infrastructure it needed to hold. And then they built models on top of that foundation and watched them actually run.",[15,964,965],{},"That is the sequence. Infrastructure first. Intelligence on top of it. Not the other way around.",{"title":187,"searchDepth":188,"depth":188,"links":967},[968,969,970,971,972],{"id":846,"depth":191,"text":847},{"id":877,"depth":191,"text":878},{"id":896,"depth":191,"text":897},{"id":927,"depth":191,"text":928},{"id":952,"depth":191,"text":953},{"type":974,"title":975,"description":976},"contact","Give Your AI the Infrastructure to Actually Run","FlowFuse connects to any protocol, runs inference at the edge, and gives OT teams the deployment tooling they need to own AI in production.","2026-03-09","Your AI pilot passed every test and stalled before production. Here is why that keeps happening, and what it actually takes to stop it.","\u002Fblog\u002F2026\u002F03\u002Fimages\u002Flast-mile-problem-ai.png",{"keywords":3,"excerpt":981},{"type":12,"value":982},[983,985],[15,984,825],{},[15,986,828],{},"\u002Fblog\u002F2026\u002F03\u002Flast-mile-problem-ai",{"title":819,"description":978},{"loc":987},"blog\u002F2026\u002F03\u002Flast-mile-problem-ai","You approved the budget. The model works. So why is it still in staging?",[223,224],"Avf6clTaWP8J3I8hR-EXMK6AMIuM86LVyQ5ol4R38mc",{"id":995,"title":996,"authors":997,"body":998,"cta":1134,"date":1137,"description":1138,"extension":207,"image":1139,"lastUpdated":3,"meta":1140,"navigation":216,"path":1146,"seo":1147,"sitemap":1148,"stem":1149,"subtitle":1150,"tags":1151,"tldr":1152,"video":3,"__hash__":1153},"blog\u002Fblog\u002F2026\u002F02\u002Fedge-ai-is-80-percent-pipeline-and-20-percent-ai.md","Edge AI Is 80% Plumbing, 20% Intelligence",[10],{"type":12,"value":999,"toc":1132},[1000,1003,1006,1009,1012,1015,1023,1026,1029,1032,1035,1038,1045,1048,1051,1054,1057,1060,1063,1074,1080,1083,1086,1089,1112,1115,1118,1121,1124,1127],[15,1001,1002],{},"The model is the easy part. I know that is not what you were told. But it is true, and somewhere between your third deployment and your first production fire, you will stop arguing with it.",[15,1004,1005],{},"Edge AI is infrastructure work. Unglamorous, load-bearing, invisible-until-it-breaks infrastructure work with a neural network sitting on top of it like a trophy on a foundation nobody inspected. The 20% is the trophy. The 80% is everything underneath it.",[15,1007,1008],{},"Most Edge AI projects do not fail because the model was wrong. They fail because nobody budgeted for the plumbing, nobody respected the plumbing, and everybody assumed the plumbing would figure itself out.",[15,1010,1011],{},"It does not figure itself out.",[15,1013,1014],{},"Here is what I have watched happen, repeatedly, across manufacturing facilities that were serious about Edge AI, staffed it well, and still could not get past the pilot: the model worked. The demo worked. The business case was real. And then the project stalled. Not because the technology failed, but because the infrastructure the technology needed to survive in an actual plant was never built.",[15,1016,1017,1022],{},[22,1018,1021],{"href":1019,"rel":1020},"https:\u002F\u002Fwww.mckinsey.com\u002Fcapabilities\u002Fpeople-and-organizational-performance\u002Four-insights\u002Fthe-organization-blog\u002Favoid-pilot-purgatory-in-7-steps",[445],"McKinsey put a number on this: 84% of manufacturers pursuing IIoT were stuck in pilot mode",". More than a quarter for over two years. In the years since, the models have gotten better, the hardware has gotten cheaper, and the percentage has not moved. That tells you the model was never the constraint.",[15,1024,1025],{},"What is the constraint? Data your system can actually trust. Updates that reach hardware you cannot physically touch. Security that was designed in, not bolted on. Monitoring that catches a drifting model before it causes a quality escape. And an operating model that answers, before something breaks at 3 a.m., who owns this: IT or OT.",[15,1027,1028],{},"None of that is in the vendor's proposal. All of it determines whether the vendor's proposal is worth anything.",[15,1030,1031],{},"The factory your AI vendor designed their solution for has clean data, modern equipment, stable connectivity, and IT and OT teams working from a shared operating model. That factory is a useful abstraction. It is not your plant.",[15,1033,1034],{},"Your plant has PLCs from three different vendors. A historian configured in 2009 that your OT team will not let anyone touch because the last time someone touched it, production stopped for four hours. Legacy equipment on the shop floor with no digital interface, because when it was commissioned, \"digital interface\" was not a specification category that existed. Sensor data in proprietary formats. Timestamps that do not align across systems. Protocols that your IT team has never heard of and your OT team has been working around for a decade.",[15,1036,1037],{},"Before a single inference runs at the edge, someone has to collect and normalize data from all of that. Protocol translation. Context tagging. Historian integration. That work is months of engineering. It is almost never scoped. And when it surfaces, it is always described as a surprise, even though everyone in the plant knew it was there.",[15,1039,1040,1041,1044],{},"This is why ",[22,1042,438],{"href":1043},"\u002Fnode-red\u002F"," matters in manufacturing in a way that nothing else quite does. It was built for exactly this problem: connecting things that were never designed to talk to each other. Modbus, OPC-UA, Siemens S7, MQTT. Thousands of community-built nodes covering the full reality of what is on the factory floor, not the idealized version. Your OT engineers, the people who actually understand the equipment, can build integration flows without waiting for scarce software developers. The domain knowledge that has been locked in people's heads for years can finally become logic that runs.",[15,1046,1047],{},"But Node-RED alone is a development tool. Running it in production, across a fleet of edge devices in multiple facilities, is a different problem entirely. And that gap, between a working flow on one machine and a reliable, managed, auditable deployment across your entire operation, is precisely where most IIoT projects quietly fall apart.",[15,1049,1050],{},"FlowFuse was built to close that gap. Both gaps, actually. Because the problem in manufacturing is not just that the infrastructure is hard. It is that building the intelligence on top of it is also harder than the demos suggest, and most teams are doing both with the wrong tools.",[15,1052,1053],{},"The OT engineer who has spent fifteen years learning one plant's quirks is not going to become a software developer. That was never a realistic ask. But they understand the equipment better than anyone who might be hired to build integrations for it, and if the tooling respects that knowledge, they can do the integration work themselves. Node-RED was the first tool in this space that actually respected that. Not because it simplified the problem, but because it let domain expertise drive the solution.",[15,1055,1056],{},"FlowFuse starts from that same premise and takes it further, into the territory Node-RED was never designed to handle alone.",[15,1058,1059],{},"Take what happens when you want to put a model in production. You have a data scientist who trained something useful, a predictive maintenance model, an anomaly detector, a vision system for defect classification. The model is accurate. It works on their laptop. And then there is the question of where it actually lives, how OT can interact with it, what happens when it needs to be retrained, and who owns it when something goes wrong at 2 a.m. on a Saturday.",[15,1061,1062],{},"In most deployments, nobody has a good answer to any of those questions. The model ends up in a container somewhere that only the data scientist understands, connected to the plant by a fragile handshake that no one wants to touch. The OT team treats it like a black box because it is a black box.",[15,1064,1065,1069,1070,1073],{},[22,1066,1068],{"href":1067},"\u002Fnode-red\u002Fflowfuse\u002Fai\u002Fonxx\u002F","FlowFuse's ONNX nodes"," change that by putting the model where OT engineers already work. You train, you export, you deploy it as a node in a flow, alongside the Modbus reads, the historian writes, the MQTT publishes. The inference runs locally, on the edge device, no cloud round trip, no latency the line cannot afford. When we ",[22,1071,1072],{"href":658},"deployed a motor anomaly detector this way",", the thing that changed was not the model's accuracy. It was that the people running the line could see what the model was looking at, wire its output to the control logic themselves, and update it through the same pipeline they use for everything else. That is not a convenience improvement. That is the difference between a model that gets maintained and a model that gets abandoned.",[15,1075,1076,1077,1079],{},"The same logic applies to the ",[22,1078,714],{"href":183},". OT engineers are not waiting for JavaScript fluency. They know the equipment; they know what they need the flow to do; they just get slowed down in the translation between that knowledge and working code. The Expert handles the boilerplate, autocompletes flows, generates function node logic from a plain-language description, explains what a set of nodes does in terms that make sense. It is not a general-purpose chatbot bolted onto an IDE. It was trained on Node-RED and FlowFuse specifically, which means it gives answers that work in industrial contexts rather than answers that look plausible until you try to run them. For teams where the backlog of integration work is longer than the list of people who can do it, that matters.",[15,1081,1082],{},"And then there is the part that breaks most programs before they get to ask any of these questions: operating at fleet scale.",[15,1084,1085],{},"One Node-RED instance, on one machine, managed by the person who set it up, is survivable. Ten devices across two facilities, or a hundred devices across a global operation, is a different problem. You need to push an update to a device on a production line in a facility in another country, and you need to know it landed correctly, and you need to be able to roll it back in under five minutes if it did not. You need to prove, to an auditor or a regulator, exactly which software version was running on which device at which moment. You need to be sure that when a device is decommissioned, its credentials are gone and its configuration is immediately invalidated.",[15,1087,1088],{},"None of that is possible with stock Node-RED. All of it is table stakes in a real manufacturing environment.",[15,1090,1091,1092,1096,1097,1101,1102,1106,1107,1111],{},"FlowFuse's ",[22,1093,1095],{"href":1094},"\u002Fblog\u002F2024\u002F09\u002Fnode-red-version-control-with-snapshots\u002F","snapshot-based"," deployments give you a tested, versioned, rollback-capable pipeline for every device in your fleet. ",[22,1098,1100],{"href":1099},"\u002Fblog\u002F2024\u002F10\u002Fhow-to-build-automate-devops-pipelines-node-red-deployments\u002F","Staged rollouts"," let you push to a test group first, validate behavior in real conditions, and then promote to production, the same discipline software engineering spent twenty years learning, now available to the people managing industrial edge infrastructure. ",[22,1103,1105],{"href":1104},"\u002Fblog\u002F2024\u002F04\u002Frole-based-access-control-rbac-for-node-red-with-flowfuse\u002F","Role-based access control",", ",[22,1108,1110],{"href":1109},"\u002Fblog\u002F2024\u002F07\u002Fhow-to-setup-sso-saml-for-the-node-red\u002F","SSO integration",", and a complete audit trail are in the architecture, not added later when someone asks for them. In automotive, pharmaceutical, and food manufacturing, where you need to prove exactly which software version was running on which device at which moment, that audit trail is not a reporting feature. It is compliance infrastructure.",[15,1113,1114],{},"The manufacturers who escape pilot purgatory are not the ones with better models. They are the ones who decided, before the model was ever deployed, that the infrastructure was the product. The model is a feature. What makes the feature reliable, observable, and maintainable, across a fleet of heterogeneous devices, in real plants, over years, is everything underneath it.",[15,1116,1117],{},"FlowFuse is that infrastructure. Not because it is the most technically sophisticated platform available, but because it was built for the actual factory floor: the PLCs from three vendors, the historian nobody wants to touch, the protocols IT has never heard of, the OT engineers who know more about this equipment than anyone who might be hired to replace them. It meets the plant where it is. That is a harder design constraint than building for the idealized version.",[15,1119,1120],{},"The conference circuit will keep celebrating the 20%. The benchmark results, the accuracy curves, the inference speeds. That work matters. But it is not what separates the manufacturers generating real operational value from the ones still running the same pilot they started two years ago.",[15,1122,1123],{},"What separates them is the plumbing.",[15,1125,1126],{},"Build it on something that understands where you are starting from.",[15,1128,1129],{},[35,1130,1131],{},"If you are running Node-RED in production today, or trying to, FlowFuse is the operational layer that makes it scale. Device management, snapshot deployments, DevOps pipelines, ONNX-based AI inference, and the FlowFuse Expert to build faster, all built specifically for industrial environments. [Start a 30-day free trial today]({% include \"sign-up-url.njk\" %}). No abstractions. No greenfield assumptions. Just the infrastructure your plant actually needs.",{"title":187,"searchDepth":188,"depth":188,"links":1133},[],{"type":202,"title":1135,"description":1136},"Build the Plumbing That Makes AI Actually Work","FlowFuse is the operational layer that takes Node-RED to production, device management, snapshot deployments, ONNX-based AI inference, and DevOps pipelines built specifically for the factory floor.","2026-02-27","Learn how manufacturers are turning Edge AI pilots into production reality, and why the plumbing matters more than the model.","\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fedge-ai-is-80-20.png",{"keywords":1141,"excerpt":1142},"Edge AI, Industrial IoT, IIoT, manufacturing AI, Edge AI deployment, AI pilot to production, Node-RED, FlowFuse, ONNX, predictive maintenance, anomaly detection, edge device management, industrial automation, OT IT convergence",{"type":12,"value":1143},[1144],[15,1145,1002],{},"\u002Fblog\u002F2026\u002F02\u002Fedge-ai-is-80-percent-pipeline-and-20-percent-ai",{"title":996,"description":1138},{"loc":1146},"blog\u002F2026\u002F02\u002Fedge-ai-is-80-percent-pipeline-and-20-percent-ai","Why your Edge AI pilot is still a pilot.",[223,224],"Edge AI projects stall not because models are inaccurate, but because the infrastructure underneath them data normalization, fleet management, secure updates, and monitoring was never properly built. FlowFuse and Node-RED together provide the operational layer that bridges the gap between a working pilot and a reliable production deployment across real factory environments.","X72LWHUG_3L99GxMHzV4MXyWWGER08O8dYdktbGefVI",{"id":1155,"title":1156,"authors":1157,"body":1158,"cta":5535,"date":5538,"description":5539,"extension":207,"image":5540,"lastUpdated":5541,"meta":5542,"navigation":216,"path":5547,"seo":5548,"sitemap":5549,"stem":5550,"subtitle":5551,"tags":5552,"tldr":5553,"video":3,"__hash__":5554},"blog\u002Fblog\u002F2026\u002F02\u002Fmotor-anomaly-detector-ai.md","Building an AI Vibration Anomaly Detector for Industrial Motors",[10],{"type":12,"value":1159,"toc":5512},[1160,1163,1166,1169,1174,1178,1181,1185,1194,1203,1206,1209,1213,1216,1220,1223,1227,1230,1415,1418,1427,1435,1439,1442,1466,1470,1473,1477,1483,1486,1517,1532,1535,1566,1570,1573,1730,1749,1753,1764,3012,3019,3038,3050,3054,3057,3061,3064,3068,3071,3083,3104,3111,3117,3121,3124,3178,3182,3185,3190,3197,3205,3210,3229,4662,4672,4683,4688,4693,4712,4718,4723,4729,5333,5343,5351,5354,5360,5365,5387,5391,5394,5469,5486,5490,5493,5496,5499,5505,5508],[15,1161,1162],{},"Bearing wear, shaft misalignment, and imbalance don't appear overnight. They develop over days or weeks, leaving a clear trail in vibration data long before any audible or thermal symptoms emerge. By the time a technician hears grinding or feels heat, the window for low-cost intervention has already closed.",[15,1164,1165],{},"The challenge isn't visibility: it's continuity. Manual spot-checks capture a fraction of developing faults, and only if the timing is lucky. What's needed is something that watches constantly, understands what normal looks like, and flags the moment something shifts.",[15,1167,1168],{},"This guide walks through building exactly that: a custom AI model that learns the healthy vibration signature of your motor, detects deviations in real time, and integrates directly into Node-RED using FlowFuse with no separate ML infrastructure required.",[127,1170],{"videoid":1171,"style":1172,"title":1173},"Fkv2x3Kv0lY","width: 100%; aspect-ratio: 16\u002F9; background-image: url('\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fanomaly-detection.png'); background-size: cover; background-position: center;","Motor Anomaly Detection System Built Using FlowFuse",[39,1175,1177],{"id":1176},"how-it-works","How It Works",[15,1179,1180],{},"An accelerometer mounted on the motor captures vibration across three axes (X, Y, Z) and publishes batches of raw readings to an MQTT broker every half-second. A Node-RED flow subscribes to those readings, extracts 33 statistical features per batch (covering energy, peak forces, shape, and distribution across all three axes) and passes them to a trained autoencoder.",[143,1182,1184],{"id":1183},"why-an-autoencoder","Why an Autoencoder?",[15,1186,1187,1188,1193],{},"An ",[22,1189,1192],{"href":1190,"rel":1191},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FAutoencoder",[445],"autoencoder"," is a neural network trained to compress its input and then reconstruct it. The architecture used here is:",[1195,1196,1201],"pre",{"className":1197,"code":1199,"language":1200},[1198],"language-text","Input (33) → Dense (16) → Dense (8) → Dense (16) → Output (33)\n","text",[76,1202,1199],{"__ignoreMap":187},[15,1204,1205],{},"The bottleneck layer (8 nodes) forces the model to learn a compact representation of the input. When trained exclusively on healthy motor data, the model learns to reconstruct normal vibration patterns with very low error. When conditions change (a bearing begins to wear, alignment drifts, imbalance develops) the vibration signature shifts, reconstruction error rises, and the system flags an anomaly.",[15,1207,1208],{},"This approach is well-suited to industrial use because you almost certainly have abundant examples of normal operation, but few or no labeled examples of specific fault modes. You don't need to know what failure looks like; you only need to define what normal looks like.",[39,1210,1212],{"id":1211},"building-the-system","Building the System",[15,1214,1215],{},"The implementation has three stages: setting up hardware to collect vibration data, training the autoencoder on normal operation, and deploying the trained model in Node-RED for real-time inference.",[39,1217,1219],{"id":1218},"part-1-hardware-and-data-requirements","Part 1: Hardware and Data Requirements",[15,1221,1222],{},"This guide assumes you already have a vibration sensor publishing batches of acceleration readings across X, Y, and Z axes at regular intervals. The examples were built using an ESP32 wired to an ADXL345 accelerometer. If your hardware differs, the rest of the steps remain unchanged as long as your sensor publishes the same payload format.",[143,1224,1226],{"id":1225},"expected-payload-format","Expected Payload Format",[15,1228,1229],{},"Each MQTT message contains a half-second snapshot of motor vibration. The sensor captures 256 measurements per axis and packages them into a single JSON payload:",[1195,1231,1235],{"className":1232,"code":1233,"language":1234,"meta":187,"style":187},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"motor_id\": \"motor-01\",\n  \"ts\": 1718000000000,\n  \"x\": [0.12, 0.11, 0.13, 0.14, ...],\n  \"y\": [0.04, 0.05, 0.04, 0.03, ...],\n  \"z\": [0.98, 0.97, 0.99, 0.96, ...]\n}\n","json",[76,1236,1237,1246,1273,1290,1332,1370,1409],{"__ignoreMap":187},[1238,1239,1242],"span",{"class":1240,"line":1241},"line",1,[1238,1243,1245],{"class":1244},"sMK4o","{\n",[1238,1247,1248,1251,1255,1258,1261,1264,1268,1270],{"class":1240,"line":191},[1238,1249,1250],{"class":1244},"  \"",[1238,1252,1254],{"class":1253},"spNyl","motor_id",[1238,1256,1257],{"class":1244},"\"",[1238,1259,1260],{"class":1244},":",[1238,1262,1263],{"class":1244}," \"",[1238,1265,1267],{"class":1266},"sfazB","motor-01",[1238,1269,1257],{"class":1244},[1238,1271,1272],{"class":1244},",\n",[1238,1274,1275,1277,1280,1282,1284,1288],{"class":1240,"line":196},[1238,1276,1250],{"class":1244},[1238,1278,1279],{"class":1253},"ts",[1238,1281,1257],{"class":1244},[1238,1283,1260],{"class":1244},[1238,1285,1287],{"class":1286},"sbssI"," 1718000000000",[1238,1289,1272],{"class":1244},[1238,1291,1292,1294,1297,1299,1301,1304,1307,1310,1313,1315,1318,1320,1323,1325,1329],{"class":1240,"line":188},[1238,1293,1250],{"class":1244},[1238,1295,1296],{"class":1253},"x",[1238,1298,1257],{"class":1244},[1238,1300,1260],{"class":1244},[1238,1302,1303],{"class":1244}," [",[1238,1305,1306],{"class":1286},"0.12",[1238,1308,1309],{"class":1244},",",[1238,1311,1312],{"class":1286}," 0.11",[1238,1314,1309],{"class":1244},[1238,1316,1317],{"class":1286}," 0.13",[1238,1319,1309],{"class":1244},[1238,1321,1322],{"class":1286}," 0.14",[1238,1324,1309],{"class":1244},[1238,1326,1328],{"class":1327},"sTEyZ"," ...",[1238,1330,1331],{"class":1244},"],\n",[1238,1333,1335,1337,1340,1342,1344,1346,1349,1351,1354,1356,1359,1361,1364,1366,1368],{"class":1240,"line":1334},5,[1238,1336,1250],{"class":1244},[1238,1338,1339],{"class":1253},"y",[1238,1341,1257],{"class":1244},[1238,1343,1260],{"class":1244},[1238,1345,1303],{"class":1244},[1238,1347,1348],{"class":1286},"0.04",[1238,1350,1309],{"class":1244},[1238,1352,1353],{"class":1286}," 0.05",[1238,1355,1309],{"class":1244},[1238,1357,1358],{"class":1286}," 0.04",[1238,1360,1309],{"class":1244},[1238,1362,1363],{"class":1286}," 0.03",[1238,1365,1309],{"class":1244},[1238,1367,1328],{"class":1327},[1238,1369,1331],{"class":1244},[1238,1371,1373,1375,1378,1380,1382,1384,1387,1389,1392,1394,1397,1399,1402,1404,1406],{"class":1240,"line":1372},6,[1238,1374,1250],{"class":1244},[1238,1376,1377],{"class":1253},"z",[1238,1379,1257],{"class":1244},[1238,1381,1260],{"class":1244},[1238,1383,1303],{"class":1244},[1238,1385,1386],{"class":1286},"0.98",[1238,1388,1309],{"class":1244},[1238,1390,1391],{"class":1286}," 0.97",[1238,1393,1309],{"class":1244},[1238,1395,1396],{"class":1286}," 0.99",[1238,1398,1309],{"class":1244},[1238,1400,1401],{"class":1286}," 0.96",[1238,1403,1309],{"class":1244},[1238,1405,1328],{"class":1327},[1238,1407,1408],{"class":1244},"]\n",[1238,1410,1412],{"class":1240,"line":1411},7,[1238,1413,1414],{"class":1244},"}\n",[15,1416,1417],{},"At 500 Hz sampling, 256 values represent roughly half a second of continuous vibration. This batching approach matters because it gives the AI model enough context to detect patterns: a single data point is meaningless, but 256 points reveal the behavioral signature of how the motor is actually running.",[15,1419,1420,1421,1423,1424,1426],{},"The ",[76,1422,1254],{}," and ",[76,1425,1279],{}," fields are ignored by the model and can be omitted or renamed without effect.",[118,1428,1429],{},[15,1430,1431,1434],{},[53,1432,1433],{},"If your sensor uses different settings:"," The feature extraction math works regardless of sample count or sampling rate. If your sensor samples at 200 Hz and sends 128 values per batch, each window represents 640 ms instead of 500 ms; the model doesn't care about absolute timing, only the shape of the vibration signature. Aim for at least 100–200 ms of data per window; anything shorter may not carry enough signal for reliable detection.",[143,1436,1438],{"id":1437},"mqtt-broker","MQTT Broker",[15,1440,1441],{},"You'll need an MQTT broker to route messages between the sensor, the training script, and Node-RED. Make sure your sensor is publishing to a consistent topic so all three can stay in sync.",[118,1443,1444],{},[15,1445,1446,1449,1450,1454,1455,1423,1458,1461,1462,1465],{},[53,1447,1448],{},"Tip:"," If you're using ",[22,1451,1453],{"href":1452},"\u002F","FlowFuse"," for enterprise Node-RED, a built-in MQTT broker is available on ",[53,1456,1457],{},"Pro",[53,1459,1460],{},"Enterprise"," tiers with no external setup required. ",[22,1463,262],{"href":1464},"\u002Fcontact-us"," for more information.",[39,1467,1469],{"id":1468},"part-2-training-the-autoencoder","Part 2: Training the Autoencoder",[15,1471,1472],{},"Before deploying anything in Node-RED, you need a trained model that understands what normal motor vibration looks like. This is done with a single Python script that connects to your MQTT broker, collects vibration data while the motor runs normally, then automatically trains and exports the model when you're done.",[143,1474,1476],{"id":1475},"prerequisites","Prerequisites",[15,1478,1479,1482],{},[53,1480,1481],{},"System requirements:"," Python 3.11 or later. The steps below were tested on macOS (Apple Silicon); adapt as needed for Linux or Windows.",[15,1484,1485],{},"Create and activate a virtual environment:",[1195,1487,1491],{"className":1488,"code":1489,"language":1490,"meta":187,"style":187},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","python3 -m venv venv\nsource venv\u002Fbin\u002Factivate\n","bash",[76,1492,1493,1508],{"__ignoreMap":187},[1238,1494,1495,1499,1502,1505],{"class":1240,"line":1241},[1238,1496,1498],{"class":1497},"sBMFI","python3",[1238,1500,1501],{"class":1266}," -m",[1238,1503,1504],{"class":1266}," venv",[1238,1506,1507],{"class":1266}," venv\n",[1238,1509,1510,1514],{"class":1240,"line":191},[1238,1511,1513],{"class":1512},"s2Zo4","source",[1238,1515,1516],{"class":1266}," venv\u002Fbin\u002Factivate\n",[118,1518,1519],{},[15,1520,1521,1524,1525,1528,1529,474],{},[53,1522,1523],{},"Windows:"," Replace ",[76,1526,1527],{},"source venv\u002Fbin\u002Factivate"," with ",[76,1530,1531],{},"venv\\Scripts\\activate",[15,1533,1534],{},"Install dependencies:",[1195,1536,1538],{"className":1488,"code":1537,"language":1490,"meta":187,"style":187},"pip3 install numpy paho-mqtt torch onnx onnxruntime scikit-learn\n",[76,1539,1540],{"__ignoreMap":187},[1238,1541,1542,1545,1548,1551,1554,1557,1560,1563],{"class":1240,"line":1241},[1238,1543,1544],{"class":1497},"pip3",[1238,1546,1547],{"class":1266}," install",[1238,1549,1550],{"class":1266}," numpy",[1238,1552,1553],{"class":1266}," paho-mqtt",[1238,1555,1556],{"class":1266}," torch",[1238,1558,1559],{"class":1266}," onnx",[1238,1561,1562],{"class":1266}," onnxruntime",[1238,1564,1565],{"class":1266}," scikit-learn\n",[143,1567,1569],{"id":1568},"configuration","Configuration",[15,1571,1572],{},"All setup lives in a single configuration block at the top of the script. Before running, update these variables to match your environment:",[1574,1575,1576,1589],"table",{},[1577,1578,1579],"thead",{},[1580,1581,1582,1586],"tr",{},[1583,1584,1585],"th",{},"Variable",[1583,1587,1588],{},"Description",[1590,1591,1592,1603,1620,1634,1646,1656,1666,1676,1686,1696,1706,1716],"tbody",{},[1580,1593,1594,1600],{},[1595,1596,1597],"td",{},[76,1598,1599],{},"BROKER",[1595,1601,1602],{},"Hostname or IP of your MQTT broker",[1580,1604,1605,1610],{},[1595,1606,1607],{},[76,1608,1609],{},"PORT",[1595,1611,1612,1615,1616,1619],{},[76,1613,1614],{},"1883"," for plain MQTT, ",[76,1617,1618],{},"8883"," for TLS",[1580,1621,1622,1627],{},[1595,1623,1624],{},[76,1625,1626],{},"USERNAME",[1595,1628,1629,1630,1633],{},"Broker username. Leave empty ",[76,1631,1632],{},"\"\""," if not required",[1580,1635,1636,1641],{},[1595,1637,1638],{},[76,1639,1640],{},"PASSWORD",[1595,1642,1643,1644,1633],{},"Broker password. Leave empty ",[76,1645,1632],{},[1580,1647,1648,1653],{},[1595,1649,1650],{},[76,1651,1652],{},"CLIENT_ID",[1595,1654,1655],{},"Any unique string identifying this client",[1580,1657,1658,1663],{},[1595,1659,1660],{},[76,1661,1662],{},"TOPIC",[1595,1664,1665],{},"The MQTT topic your sensor publishes to",[1580,1667,1668,1673],{},[1595,1669,1670],{},[76,1671,1672],{},"MIN_WINDOWS",[1595,1674,1675],{},"Minimum samples to collect before training (default: 300)",[1580,1677,1678,1683],{},[1595,1679,1680],{},[76,1681,1682],{},"MIN_STD",[1595,1684,1685],{},"Minimum standard deviation floor that prevents near-constant features from skewing normalisation (default: 0.1)",[1580,1687,1688,1693],{},[1595,1689,1690],{},[76,1691,1692],{},"CLIP",[1595,1694,1695],{},"Hard clamp applied after normalisation to prevent extreme values (default: 5.0)",[1580,1697,1698,1703],{},[1595,1699,1700],{},[76,1701,1702],{},"EPOCHS",[1595,1704,1705],{},"Number of training epochs (default: 200)",[1580,1707,1708,1713],{},[1595,1709,1710],{},[76,1711,1712],{},"LEARNING_RATE",[1595,1714,1715],{},"Adam optimizer learning rate (default: 0.001)",[1580,1717,1718,1723],{},[1595,1719,1720],{},[76,1721,1722],{},"THRESHOLD_SIGMA",[1595,1724,1725,1726,1729],{},"Multiplier for threshold calculation: ",[76,1727,1728],{},"mean + N × std"," of training errors (default: 3)",[118,1731,1732],{},[15,1733,1734,1737,1738,1741,1742,1744,1745,1748],{},[53,1735,1736],{},"Threshold tuning:"," The default ",[76,1739,1740],{},"mean + 3σ"," threshold is a solid starting point, but every motor environment is different. If you see too many false positives during normal operation, increase ",[76,1743,1722],{},". If faults are being missed, decrease it. You can also edit ",[76,1746,1747],{},"threshold.json"," directly after training without rerunning the script.",[143,1750,1752],{"id":1751},"collect-and-train","Collect and Train",[15,1754,1755,1756,1759,1760,1763],{},"Create a file called ",[76,1757,1758],{},"train_model.py"," and paste the following. ",[53,1761,1762],{},"Start the motor first, then run the script."," The model needs to learn what running vibration looks like. Collecting data with the motor stopped or barely loaded will produce a model that treats idle conditions as normal and misses real anomalies.",[1195,1765,1769],{"className":1766,"code":1767,"language":1768,"meta":187,"style":187},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\"\"\"\nMotor Vibration Anomaly Detection: Data Collection and Training\n\"\"\"\n\nimport json\nimport signal\nimport sys\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport onnx\nimport onnxruntime as ort\nfrom onnx import numpy_helper, TensorProto, helper\nimport paho.mqtt.client as mqtt\nfrom paho.mqtt.client import CallbackAPIVersion\n\n# ── Configuration ────────────────────────────────────────────────────────────\nBROKER         = \"broker.example.com\"            # Your MQTT broker address or IP\nPORT           = 1883                            # 1883 for plain MQTT, 8883 for TLS\nUSERNAME       = \"\"                              # Leave as \"\" if broker has no auth\nPASSWORD       = \"\"                              # Leave as \"\" if broker has no auth\nCLIENT_ID      = \"motor-trainer-01\"             # Any unique string for this client\nTOPIC          = \"factory\u002Fmotor-01\u002Fvibration\u002Fraw\" # Must match your sensor's publish topic\n\n# ── Training parameters ───────────────────────────────────────────────────────\nMIN_WINDOWS    = 300    # Minimum samples before training is allowed\nMIN_STD        = 0.1    # Prevents near-constant features from exploding normalisation\nCLIP           = 5.0    # Hard clip applied after normalisation\nEPOCHS         = 200    # Number of training epochs\nLEARNING_RATE  = 1e-3   # Adam optimiser learning rate\nTHRESHOLD_SIGMA = 3     # Threshold = mean + N * std of training reconstruction errors\n# ─────────────────────────────────────────────────────────────────────────────\n\ntraining_data = []\nstop_flag = [False]\n\ndef extract_features(sig):\n    \"\"\"Extract 11 time-domain features from a signal array.\"\"\"\n    sig  = np.asarray(sig, dtype=np.float64)\n    mean = np.mean(sig)\n    std  = np.std(sig) + 1e-9\n\n    rms              = np.sqrt(np.mean(sig ** 2))\n    peak             = np.max(np.abs(sig))\n    peak_to_peak     = np.max(sig) - np.min(sig)\n    crest_factor     = peak \u002F (rms + 1e-9)\n    variance         = np.var(sig)\n    std_dev          = np.std(sig)\n    skewness         = np.mean(((sig - mean) \u002F std) ** 3)\n    kurtosis         = np.mean(((sig - mean) \u002F std) ** 4) - 3\n    mean_abs         = np.mean(np.abs(sig)) + 1e-9\n    shape_factor     = rms \u002F mean_abs\n    impulse_factor   = peak \u002F mean_abs\n    mean_sqrt_abs    = np.mean(np.sqrt(np.abs(sig)))\n    clearance_factor = peak \u002F (mean_sqrt_abs ** 2 + 1e-9)\n\n    return [rms, peak, peak_to_peak, crest_factor, variance,\n            std_dev, skewness, kurtosis, shape_factor,\n            impulse_factor, clearance_factor]\n\ndef featurize(payload):\n    \"\"\"Concatenate features from X, Y, Z → 33-element vector.\"\"\"\n    return (extract_features(payload[\"x\"]) +\n            extract_features(payload[\"y\"]) +\n            extract_features(payload[\"z\"]))\n\ndef on_connect(client, userdata, flags, reason_code, properties):\n    if reason_code == 0:\n        print(f\"Connected to {BROKER}\")\n        client.subscribe(TOPIC)\n        print(f\"Subscribed to {TOPIC}\")\n        print(\"Run motor normally. Press Ctrl+C when done collecting.\\n\")\n    else:\n        print(f\"Connection failed: {reason_code}\")\n\ndef on_message(client, userdata, msg):\n    if stop_flag[0]:\n        return\n    try:\n        payload = json.loads(msg.payload.decode())\n        training_data.append(featurize(payload))\n        n = len(training_data)\n        print(f\"  Collected {n} windows\", end=\"\\r\")\n    except Exception as e:\n        print(f\"\\nError parsing message: {e}\")\n\ndef train_and_export():\n    N = 33\n    print(f\"\\n\\nCollected {len(training_data)} windows. Starting training...\")\n    X = np.array(training_data, dtype=np.float32)\n\n    # Normalisation with minimum std floor\n    mean = X.mean(axis=0)\n    std  = X.std(axis=0)\n\n    clamped = std \u003C MIN_STD\n    if clamped.any():\n        print(f\"  Clamping {clamped.sum()} near-constant features to std={MIN_STD}\")\n        std[clamped] = MIN_STD\n\n    X_norm = np.clip((X - mean) \u002F std, -CLIP, CLIP)\n    print(f\"  Normalised range: [{X_norm.min():.3f}, {X_norm.max():.3f}]\")\n\n    scaler = {\"mean\": mean.tolist(), \"std\": std.tolist(), \"clip\": CLIP}\n    with open(\"scaler_params.json\", \"w\") as f:\n        json.dump(scaler, f, indent=2)\n    print(\"  Saved scaler_params.json\")\n\n    # Autoencoder definition\n    class Autoencoder(nn.Module):\n        def __init__(self, n):\n            super().__init__()\n            self.encoder = nn.Sequential(\n                nn.Linear(n, 16), nn.ReLU(),\n                nn.Linear(16, 8), nn.ReLU(),\n            )\n            self.decoder = nn.Sequential(\n                nn.Linear(8, 16), nn.ReLU(),\n                nn.Linear(16, n),\n            )\n        def forward(self, x):\n            return self.decoder(self.encoder(x))\n\n    model   = Autoencoder(N)\n    opt     = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n    loss_fn = nn.MSELoss()\n    data_t  = torch.tensor(X_norm, dtype=torch.float32)\n\n    model.train()\n    for epoch in range(1, EPOCHS + 1):\n        opt.zero_grad()\n        loss = loss_fn(model(data_t), data_t)\n        loss.backward()\n        opt.step()\n        if epoch % (EPOCHS \u002F\u002F 5) == 0:\n            print(f\"  Epoch {epoch}\u002F{EPOCHS}  loss={loss.item():.6f}\")\n\n    # Calculate threshold\n    model.eval()\n    with torch.no_grad():\n        recon  = model(data_t).numpy()\n        errors = np.mean((recon - X_norm) ** 2, axis=1)\n        thresh = float(errors.mean() + THRESHOLD_SIGMA * errors.std())\n\n    with open(\"threshold.json\", \"w\") as f:\n        json.dump({\"threshold\": thresh}, f, indent=2)\n    print(f\"  Threshold (mean+{THRESHOLD_SIGMA}σ): {thresh:.6f}\")\n    print(\"  Saved threshold.json\")\n\n    # Export to ONNX\n    # Built manually to avoid version conflicts between PyTorch and ONNX exporters\n    layers   = [(\"encoder.0\",\"enc0\"),(\"encoder.2\",\"enc2\"),\n                (\"decoder.0\",\"dec0\"),(\"decoder.2\",\"dec2\")]\n    has_relu = [True, True, True, False]\n    inits, nodes = [], []\n    cur = \"features\"\n\n    for (prefix, tag), relu in zip(layers, has_relu):\n        w = model.state_dict()[f\"{prefix}.weight\"].numpy().T.astype(np.float32)\n        b = model.state_dict()[f\"{prefix}.bias\"].numpy().astype(np.float32)\n        inits += [numpy_helper.from_array(w, name=f\"w_{tag}\"),\n                  numpy_helper.from_array(b, name=f\"b_{tag}\")]\n        mm = f\"mm_{tag}\"; add = f\"add_{tag}\"\n        nodes += [helper.make_node(\"MatMul\", [cur, f\"w_{tag}\"], [mm]),\n                  helper.make_node(\"Add\",    [mm,  f\"b_{tag}\"], [add])]\n        cur = add\n        if relu:\n            r = f\"relu_{tag}\"\n            nodes.append(helper.make_node(\"Relu\", [cur], [r]))\n            cur = r\n\n    graph = helper.make_graph(\n        nodes, \"autoencoder\",\n        [helper.make_tensor_value_info(\"features\", TensorProto.FLOAT, [None, N])],\n        [helper.make_tensor_value_info(cur, TensorProto.FLOAT, [None, N])],\n        initializer=inits,\n    )\n    proto = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 11)])\n    proto.ir_version = 7\n    onnx.checker.check_model(proto)\n    onnx.save(proto, \"motor_autoencoder.onnx\")\n    print(\"  Exported motor_autoencoder.onnx\")\n\n    # Sanity check\n    sess = ort.InferenceSession(\"motor_autoencoder.onnx\")\n    out  = sess.run(None, {\"features\": X_norm[:5].astype(np.float32)})[0]\n    mse  = float(np.mean((out - X_norm[:5]) ** 2))\n    print(f\"\\n  Sanity MSE (5 normal samples): {mse:.6f}  threshold: {thresh:.6f}\")\n    if mse \u003C thresh:\n        print(\"  ✓ Model correct , normal data scores below threshold.\")\n    else:\n        print(\"  ⚠ Sanity MSE above threshold , collect more data and retrain.\")\n\n    print(\"\\nDone. Copy these 3 files to your Node-RED server:\")\n    print(\"  motor_autoencoder.onnx  scaler_params.json  threshold.json\")\n\ndef handle_sigint(sig, frame):\n    stop_flag[0] = True\n    if len(training_data) \u003C MIN_WINDOWS:\n        print(f\"\\n\\nNeed at least {MIN_WINDOWS} windows. Restart and collect longer.\")\n        sys.exit(1)\n    train_and_export()\n    sys.exit(0)\n\nsignal.signal(signal.SIGINT, handle_sigint)\n\nclient = mqtt.Client(callback_api_version=CallbackAPIVersion.VERSION2,\n                     client_id=CLIENT_ID)\nclient.username_pw_set(USERNAME, PASSWORD)\nclient.on_connect = on_connect\nclient.on_message = on_message\nclient.connect(BROKER, PORT, keepalive=60)\nclient.loop_forever()\n","python",[76,1770,1771,1776,1781,1785,1790,1795,1800,1805,1811,1817,1823,1829,1835,1841,1847,1853,1858,1864,1870,1876,1882,1888,1894,1900,1905,1911,1917,1923,1929,1935,1941,1947,1953,1958,1964,1970,1975,1981,1987,1993,1999,2005,2010,2016,2022,2028,2034,2040,2046,2052,2058,2064,2070,2076,2082,2088,2093,2099,2105,2111,2116,2122,2128,2134,2140,2146,2151,2157,2163,2169,2175,2181,2187,2193,2199,2204,2210,2216,2222,2228,2234,2240,2246,2252,2258,2264,2269,2275,2281,2287,2293,2298,2304,2310,2316,2321,2327,2333,2339,2345,2350,2356,2362,2367,2373,2379,2385,2391,2396,2402,2408,2414,2420,2426,2432,2438,2444,2450,2456,2462,2467,2473,2479,2484,2490,2496,2502,2508,2513,2519,2525,2531,2537,2543,2549,2555,2561,2566,2572,2578,2584,2590,2596,2602,2607,2613,2619,2625,2631,2636,2642,2648,2654,2660,2666,2672,2678,2683,2689,2695,2701,2707,2713,2719,2725,2731,2737,2743,2749,2755,2761,2766,2772,2778,2784,2790,2796,2802,2808,2814,2820,2826,2832,2837,2843,2849,2855,2861,2867,2873,2879,2884,2890,2895,2901,2907,2912,2918,2924,2930,2936,2942,2948,2954,2959,2965,2970,2976,2982,2988,2994,3000,3006],{"__ignoreMap":187},[1238,1772,1773],{"class":1240,"line":1241},[1238,1774,1775],{},"\"\"\"\n",[1238,1777,1778],{"class":1240,"line":191},[1238,1779,1780],{},"Motor Vibration Anomaly Detection: Data Collection and Training\n",[1238,1782,1783],{"class":1240,"line":196},[1238,1784,1775],{},[1238,1786,1787],{"class":1240,"line":188},[1238,1788,1789],{"emptyLinePlaceholder":216},"\n",[1238,1791,1792],{"class":1240,"line":1334},[1238,1793,1794],{},"import json\n",[1238,1796,1797],{"class":1240,"line":1372},[1238,1798,1799],{},"import signal\n",[1238,1801,1802],{"class":1240,"line":1411},[1238,1803,1804],{},"import sys\n",[1238,1806,1808],{"class":1240,"line":1807},8,[1238,1809,1810],{},"import numpy as np\n",[1238,1812,1814],{"class":1240,"line":1813},9,[1238,1815,1816],{},"import torch\n",[1238,1818,1820],{"class":1240,"line":1819},10,[1238,1821,1822],{},"import torch.nn as nn\n",[1238,1824,1826],{"class":1240,"line":1825},11,[1238,1827,1828],{},"import onnx\n",[1238,1830,1832],{"class":1240,"line":1831},12,[1238,1833,1834],{},"import onnxruntime as ort\n",[1238,1836,1838],{"class":1240,"line":1837},13,[1238,1839,1840],{},"from onnx import numpy_helper, TensorProto, helper\n",[1238,1842,1844],{"class":1240,"line":1843},14,[1238,1845,1846],{},"import paho.mqtt.client as mqtt\n",[1238,1848,1850],{"class":1240,"line":1849},15,[1238,1851,1852],{},"from paho.mqtt.client import CallbackAPIVersion\n",[1238,1854,1856],{"class":1240,"line":1855},16,[1238,1857,1789],{"emptyLinePlaceholder":216},[1238,1859,1861],{"class":1240,"line":1860},17,[1238,1862,1863],{},"# ── Configuration ────────────────────────────────────────────────────────────\n",[1238,1865,1867],{"class":1240,"line":1866},18,[1238,1868,1869],{},"BROKER         = \"broker.example.com\"            # Your MQTT broker address or IP\n",[1238,1871,1873],{"class":1240,"line":1872},19,[1238,1874,1875],{},"PORT           = 1883                            # 1883 for plain MQTT, 8883 for TLS\n",[1238,1877,1879],{"class":1240,"line":1878},20,[1238,1880,1881],{},"USERNAME       = \"\"                              # Leave as \"\" if broker has no auth\n",[1238,1883,1885],{"class":1240,"line":1884},21,[1238,1886,1887],{},"PASSWORD       = \"\"                              # Leave as \"\" if broker has no auth\n",[1238,1889,1891],{"class":1240,"line":1890},22,[1238,1892,1893],{},"CLIENT_ID      = \"motor-trainer-01\"             # Any unique string for this client\n",[1238,1895,1897],{"class":1240,"line":1896},23,[1238,1898,1899],{},"TOPIC          = \"factory\u002Fmotor-01\u002Fvibration\u002Fraw\" # Must match your sensor's publish topic\n",[1238,1901,1903],{"class":1240,"line":1902},24,[1238,1904,1789],{"emptyLinePlaceholder":216},[1238,1906,1908],{"class":1240,"line":1907},25,[1238,1909,1910],{},"# ── Training parameters ───────────────────────────────────────────────────────\n",[1238,1912,1914],{"class":1240,"line":1913},26,[1238,1915,1916],{},"MIN_WINDOWS    = 300    # Minimum samples before training is allowed\n",[1238,1918,1920],{"class":1240,"line":1919},27,[1238,1921,1922],{},"MIN_STD        = 0.1    # Prevents near-constant features from exploding normalisation\n",[1238,1924,1926],{"class":1240,"line":1925},28,[1238,1927,1928],{},"CLIP           = 5.0    # Hard clip applied after normalisation\n",[1238,1930,1932],{"class":1240,"line":1931},29,[1238,1933,1934],{},"EPOCHS         = 200    # Number of training epochs\n",[1238,1936,1938],{"class":1240,"line":1937},30,[1238,1939,1940],{},"LEARNING_RATE  = 1e-3   # Adam optimiser learning rate\n",[1238,1942,1944],{"class":1240,"line":1943},31,[1238,1945,1946],{},"THRESHOLD_SIGMA = 3     # Threshold = mean + N * std of training reconstruction errors\n",[1238,1948,1950],{"class":1240,"line":1949},32,[1238,1951,1952],{},"# ─────────────────────────────────────────────────────────────────────────────\n",[1238,1954,1956],{"class":1240,"line":1955},33,[1238,1957,1789],{"emptyLinePlaceholder":216},[1238,1959,1961],{"class":1240,"line":1960},34,[1238,1962,1963],{},"training_data = []\n",[1238,1965,1967],{"class":1240,"line":1966},35,[1238,1968,1969],{},"stop_flag = [False]\n",[1238,1971,1973],{"class":1240,"line":1972},36,[1238,1974,1789],{"emptyLinePlaceholder":216},[1238,1976,1978],{"class":1240,"line":1977},37,[1238,1979,1980],{},"def extract_features(sig):\n",[1238,1982,1984],{"class":1240,"line":1983},38,[1238,1985,1986],{},"    \"\"\"Extract 11 time-domain features from a signal array.\"\"\"\n",[1238,1988,1990],{"class":1240,"line":1989},39,[1238,1991,1992],{},"    sig  = np.asarray(sig, dtype=np.float64)\n",[1238,1994,1996],{"class":1240,"line":1995},40,[1238,1997,1998],{},"    mean = np.mean(sig)\n",[1238,2000,2002],{"class":1240,"line":2001},41,[1238,2003,2004],{},"    std  = np.std(sig) + 1e-9\n",[1238,2006,2008],{"class":1240,"line":2007},42,[1238,2009,1789],{"emptyLinePlaceholder":216},[1238,2011,2013],{"class":1240,"line":2012},43,[1238,2014,2015],{},"    rms              = np.sqrt(np.mean(sig ** 2))\n",[1238,2017,2019],{"class":1240,"line":2018},44,[1238,2020,2021],{},"    peak             = np.max(np.abs(sig))\n",[1238,2023,2025],{"class":1240,"line":2024},45,[1238,2026,2027],{},"    peak_to_peak     = np.max(sig) - np.min(sig)\n",[1238,2029,2031],{"class":1240,"line":2030},46,[1238,2032,2033],{},"    crest_factor     = peak \u002F (rms + 1e-9)\n",[1238,2035,2037],{"class":1240,"line":2036},47,[1238,2038,2039],{},"    variance         = np.var(sig)\n",[1238,2041,2043],{"class":1240,"line":2042},48,[1238,2044,2045],{},"    std_dev          = np.std(sig)\n",[1238,2047,2049],{"class":1240,"line":2048},49,[1238,2050,2051],{},"    skewness         = np.mean(((sig - mean) \u002F std) ** 3)\n",[1238,2053,2055],{"class":1240,"line":2054},50,[1238,2056,2057],{},"    kurtosis         = np.mean(((sig - mean) \u002F std) ** 4) - 3\n",[1238,2059,2061],{"class":1240,"line":2060},51,[1238,2062,2063],{},"    mean_abs         = np.mean(np.abs(sig)) + 1e-9\n",[1238,2065,2067],{"class":1240,"line":2066},52,[1238,2068,2069],{},"    shape_factor     = rms \u002F mean_abs\n",[1238,2071,2073],{"class":1240,"line":2072},53,[1238,2074,2075],{},"    impulse_factor   = peak \u002F mean_abs\n",[1238,2077,2079],{"class":1240,"line":2078},54,[1238,2080,2081],{},"    mean_sqrt_abs    = np.mean(np.sqrt(np.abs(sig)))\n",[1238,2083,2085],{"class":1240,"line":2084},55,[1238,2086,2087],{},"    clearance_factor = peak \u002F (mean_sqrt_abs ** 2 + 1e-9)\n",[1238,2089,2091],{"class":1240,"line":2090},56,[1238,2092,1789],{"emptyLinePlaceholder":216},[1238,2094,2096],{"class":1240,"line":2095},57,[1238,2097,2098],{},"    return [rms, peak, peak_to_peak, crest_factor, variance,\n",[1238,2100,2102],{"class":1240,"line":2101},58,[1238,2103,2104],{},"            std_dev, skewness, kurtosis, shape_factor,\n",[1238,2106,2108],{"class":1240,"line":2107},59,[1238,2109,2110],{},"            impulse_factor, clearance_factor]\n",[1238,2112,2114],{"class":1240,"line":2113},60,[1238,2115,1789],{"emptyLinePlaceholder":216},[1238,2117,2119],{"class":1240,"line":2118},61,[1238,2120,2121],{},"def featurize(payload):\n",[1238,2123,2125],{"class":1240,"line":2124},62,[1238,2126,2127],{},"    \"\"\"Concatenate features from X, Y, Z → 33-element vector.\"\"\"\n",[1238,2129,2131],{"class":1240,"line":2130},63,[1238,2132,2133],{},"    return (extract_features(payload[\"x\"]) +\n",[1238,2135,2137],{"class":1240,"line":2136},64,[1238,2138,2139],{},"            extract_features(payload[\"y\"]) +\n",[1238,2141,2143],{"class":1240,"line":2142},65,[1238,2144,2145],{},"            extract_features(payload[\"z\"]))\n",[1238,2147,2149],{"class":1240,"line":2148},66,[1238,2150,1789],{"emptyLinePlaceholder":216},[1238,2152,2154],{"class":1240,"line":2153},67,[1238,2155,2156],{},"def on_connect(client, userdata, flags, reason_code, properties):\n",[1238,2158,2160],{"class":1240,"line":2159},68,[1238,2161,2162],{},"    if reason_code == 0:\n",[1238,2164,2166],{"class":1240,"line":2165},69,[1238,2167,2168],{},"        print(f\"Connected to {BROKER}\")\n",[1238,2170,2172],{"class":1240,"line":2171},70,[1238,2173,2174],{},"        client.subscribe(TOPIC)\n",[1238,2176,2178],{"class":1240,"line":2177},71,[1238,2179,2180],{},"        print(f\"Subscribed to {TOPIC}\")\n",[1238,2182,2184],{"class":1240,"line":2183},72,[1238,2185,2186],{},"        print(\"Run motor normally. Press Ctrl+C when done collecting.\\n\")\n",[1238,2188,2190],{"class":1240,"line":2189},73,[1238,2191,2192],{},"    else:\n",[1238,2194,2196],{"class":1240,"line":2195},74,[1238,2197,2198],{},"        print(f\"Connection failed: {reason_code}\")\n",[1238,2200,2202],{"class":1240,"line":2201},75,[1238,2203,1789],{"emptyLinePlaceholder":216},[1238,2205,2207],{"class":1240,"line":2206},76,[1238,2208,2209],{},"def on_message(client, userdata, msg):\n",[1238,2211,2213],{"class":1240,"line":2212},77,[1238,2214,2215],{},"    if stop_flag[0]:\n",[1238,2217,2219],{"class":1240,"line":2218},78,[1238,2220,2221],{},"        return\n",[1238,2223,2225],{"class":1240,"line":2224},79,[1238,2226,2227],{},"    try:\n",[1238,2229,2231],{"class":1240,"line":2230},80,[1238,2232,2233],{},"        payload = json.loads(msg.payload.decode())\n",[1238,2235,2237],{"class":1240,"line":2236},81,[1238,2238,2239],{},"        training_data.append(featurize(payload))\n",[1238,2241,2243],{"class":1240,"line":2242},82,[1238,2244,2245],{},"        n = len(training_data)\n",[1238,2247,2249],{"class":1240,"line":2248},83,[1238,2250,2251],{},"        print(f\"  Collected {n} windows\", end=\"\\r\")\n",[1238,2253,2255],{"class":1240,"line":2254},84,[1238,2256,2257],{},"    except Exception as e:\n",[1238,2259,2261],{"class":1240,"line":2260},85,[1238,2262,2263],{},"        print(f\"\\nError parsing message: {e}\")\n",[1238,2265,2267],{"class":1240,"line":2266},86,[1238,2268,1789],{"emptyLinePlaceholder":216},[1238,2270,2272],{"class":1240,"line":2271},87,[1238,2273,2274],{},"def train_and_export():\n",[1238,2276,2278],{"class":1240,"line":2277},88,[1238,2279,2280],{},"    N = 33\n",[1238,2282,2284],{"class":1240,"line":2283},89,[1238,2285,2286],{},"    print(f\"\\n\\nCollected {len(training_data)} windows. Starting training...\")\n",[1238,2288,2290],{"class":1240,"line":2289},90,[1238,2291,2292],{},"    X = np.array(training_data, dtype=np.float32)\n",[1238,2294,2296],{"class":1240,"line":2295},91,[1238,2297,1789],{"emptyLinePlaceholder":216},[1238,2299,2301],{"class":1240,"line":2300},92,[1238,2302,2303],{},"    # Normalisation with minimum std floor\n",[1238,2305,2307],{"class":1240,"line":2306},93,[1238,2308,2309],{},"    mean = X.mean(axis=0)\n",[1238,2311,2313],{"class":1240,"line":2312},94,[1238,2314,2315],{},"    std  = X.std(axis=0)\n",[1238,2317,2319],{"class":1240,"line":2318},95,[1238,2320,1789],{"emptyLinePlaceholder":216},[1238,2322,2324],{"class":1240,"line":2323},96,[1238,2325,2326],{},"    clamped = std \u003C MIN_STD\n",[1238,2328,2330],{"class":1240,"line":2329},97,[1238,2331,2332],{},"    if clamped.any():\n",[1238,2334,2336],{"class":1240,"line":2335},98,[1238,2337,2338],{},"        print(f\"  Clamping {clamped.sum()} near-constant features to std={MIN_STD}\")\n",[1238,2340,2342],{"class":1240,"line":2341},99,[1238,2343,2344],{},"        std[clamped] = MIN_STD\n",[1238,2346,2348],{"class":1240,"line":2347},100,[1238,2349,1789],{"emptyLinePlaceholder":216},[1238,2351,2353],{"class":1240,"line":2352},101,[1238,2354,2355],{},"    X_norm = np.clip((X - mean) \u002F std, -CLIP, CLIP)\n",[1238,2357,2359],{"class":1240,"line":2358},102,[1238,2360,2361],{},"    print(f\"  Normalised range: [{X_norm.min():.3f}, {X_norm.max():.3f}]\")\n",[1238,2363,2365],{"class":1240,"line":2364},103,[1238,2366,1789],{"emptyLinePlaceholder":216},[1238,2368,2370],{"class":1240,"line":2369},104,[1238,2371,2372],{},"    scaler = {\"mean\": mean.tolist(), \"std\": std.tolist(), \"clip\": CLIP}\n",[1238,2374,2376],{"class":1240,"line":2375},105,[1238,2377,2378],{},"    with open(\"scaler_params.json\", \"w\") as f:\n",[1238,2380,2382],{"class":1240,"line":2381},106,[1238,2383,2384],{},"        json.dump(scaler, f, indent=2)\n",[1238,2386,2388],{"class":1240,"line":2387},107,[1238,2389,2390],{},"    print(\"  Saved scaler_params.json\")\n",[1238,2392,2394],{"class":1240,"line":2393},108,[1238,2395,1789],{"emptyLinePlaceholder":216},[1238,2397,2399],{"class":1240,"line":2398},109,[1238,2400,2401],{},"    # Autoencoder definition\n",[1238,2403,2405],{"class":1240,"line":2404},110,[1238,2406,2407],{},"    class Autoencoder(nn.Module):\n",[1238,2409,2411],{"class":1240,"line":2410},111,[1238,2412,2413],{},"        def __init__(self, n):\n",[1238,2415,2417],{"class":1240,"line":2416},112,[1238,2418,2419],{},"            super().__init__()\n",[1238,2421,2423],{"class":1240,"line":2422},113,[1238,2424,2425],{},"            self.encoder = nn.Sequential(\n",[1238,2427,2429],{"class":1240,"line":2428},114,[1238,2430,2431],{},"                nn.Linear(n, 16), nn.ReLU(),\n",[1238,2433,2435],{"class":1240,"line":2434},115,[1238,2436,2437],{},"                nn.Linear(16, 8), nn.ReLU(),\n",[1238,2439,2441],{"class":1240,"line":2440},116,[1238,2442,2443],{},"            )\n",[1238,2445,2447],{"class":1240,"line":2446},117,[1238,2448,2449],{},"            self.decoder = nn.Sequential(\n",[1238,2451,2453],{"class":1240,"line":2452},118,[1238,2454,2455],{},"                nn.Linear(8, 16), nn.ReLU(),\n",[1238,2457,2459],{"class":1240,"line":2458},119,[1238,2460,2461],{},"                nn.Linear(16, n),\n",[1238,2463,2465],{"class":1240,"line":2464},120,[1238,2466,2443],{},[1238,2468,2470],{"class":1240,"line":2469},121,[1238,2471,2472],{},"        def forward(self, x):\n",[1238,2474,2476],{"class":1240,"line":2475},122,[1238,2477,2478],{},"            return self.decoder(self.encoder(x))\n",[1238,2480,2482],{"class":1240,"line":2481},123,[1238,2483,1789],{"emptyLinePlaceholder":216},[1238,2485,2487],{"class":1240,"line":2486},124,[1238,2488,2489],{},"    model   = Autoencoder(N)\n",[1238,2491,2493],{"class":1240,"line":2492},125,[1238,2494,2495],{},"    opt     = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)\n",[1238,2497,2499],{"class":1240,"line":2498},126,[1238,2500,2501],{},"    loss_fn = nn.MSELoss()\n",[1238,2503,2505],{"class":1240,"line":2504},127,[1238,2506,2507],{},"    data_t  = torch.tensor(X_norm, dtype=torch.float32)\n",[1238,2509,2511],{"class":1240,"line":2510},128,[1238,2512,1789],{"emptyLinePlaceholder":216},[1238,2514,2516],{"class":1240,"line":2515},129,[1238,2517,2518],{},"    model.train()\n",[1238,2520,2522],{"class":1240,"line":2521},130,[1238,2523,2524],{},"    for epoch in range(1, EPOCHS + 1):\n",[1238,2526,2528],{"class":1240,"line":2527},131,[1238,2529,2530],{},"        opt.zero_grad()\n",[1238,2532,2534],{"class":1240,"line":2533},132,[1238,2535,2536],{},"        loss = loss_fn(model(data_t), data_t)\n",[1238,2538,2540],{"class":1240,"line":2539},133,[1238,2541,2542],{},"        loss.backward()\n",[1238,2544,2546],{"class":1240,"line":2545},134,[1238,2547,2548],{},"        opt.step()\n",[1238,2550,2552],{"class":1240,"line":2551},135,[1238,2553,2554],{},"        if epoch % (EPOCHS \u002F\u002F 5) == 0:\n",[1238,2556,2558],{"class":1240,"line":2557},136,[1238,2559,2560],{},"            print(f\"  Epoch {epoch}\u002F{EPOCHS}  loss={loss.item():.6f}\")\n",[1238,2562,2564],{"class":1240,"line":2563},137,[1238,2565,1789],{"emptyLinePlaceholder":216},[1238,2567,2569],{"class":1240,"line":2568},138,[1238,2570,2571],{},"    # Calculate threshold\n",[1238,2573,2575],{"class":1240,"line":2574},139,[1238,2576,2577],{},"    model.eval()\n",[1238,2579,2581],{"class":1240,"line":2580},140,[1238,2582,2583],{},"    with torch.no_grad():\n",[1238,2585,2587],{"class":1240,"line":2586},141,[1238,2588,2589],{},"        recon  = model(data_t).numpy()\n",[1238,2591,2593],{"class":1240,"line":2592},142,[1238,2594,2595],{},"        errors = np.mean((recon - X_norm) ** 2, axis=1)\n",[1238,2597,2599],{"class":1240,"line":2598},143,[1238,2600,2601],{},"        thresh = float(errors.mean() + THRESHOLD_SIGMA * errors.std())\n",[1238,2603,2605],{"class":1240,"line":2604},144,[1238,2606,1789],{"emptyLinePlaceholder":216},[1238,2608,2610],{"class":1240,"line":2609},145,[1238,2611,2612],{},"    with open(\"threshold.json\", \"w\") as f:\n",[1238,2614,2616],{"class":1240,"line":2615},146,[1238,2617,2618],{},"        json.dump({\"threshold\": thresh}, f, indent=2)\n",[1238,2620,2622],{"class":1240,"line":2621},147,[1238,2623,2624],{},"    print(f\"  Threshold (mean+{THRESHOLD_SIGMA}σ): {thresh:.6f}\")\n",[1238,2626,2628],{"class":1240,"line":2627},148,[1238,2629,2630],{},"    print(\"  Saved threshold.json\")\n",[1238,2632,2634],{"class":1240,"line":2633},149,[1238,2635,1789],{"emptyLinePlaceholder":216},[1238,2637,2639],{"class":1240,"line":2638},150,[1238,2640,2641],{},"    # Export to ONNX\n",[1238,2643,2645],{"class":1240,"line":2644},151,[1238,2646,2647],{},"    # Built manually to avoid version conflicts between PyTorch and ONNX exporters\n",[1238,2649,2651],{"class":1240,"line":2650},152,[1238,2652,2653],{},"    layers   = [(\"encoder.0\",\"enc0\"),(\"encoder.2\",\"enc2\"),\n",[1238,2655,2657],{"class":1240,"line":2656},153,[1238,2658,2659],{},"                (\"decoder.0\",\"dec0\"),(\"decoder.2\",\"dec2\")]\n",[1238,2661,2663],{"class":1240,"line":2662},154,[1238,2664,2665],{},"    has_relu = [True, True, True, False]\n",[1238,2667,2669],{"class":1240,"line":2668},155,[1238,2670,2671],{},"    inits, nodes = [], []\n",[1238,2673,2675],{"class":1240,"line":2674},156,[1238,2676,2677],{},"    cur = \"features\"\n",[1238,2679,2681],{"class":1240,"line":2680},157,[1238,2682,1789],{"emptyLinePlaceholder":216},[1238,2684,2686],{"class":1240,"line":2685},158,[1238,2687,2688],{},"    for (prefix, tag), relu in zip(layers, has_relu):\n",[1238,2690,2692],{"class":1240,"line":2691},159,[1238,2693,2694],{},"        w = model.state_dict()[f\"{prefix}.weight\"].numpy().T.astype(np.float32)\n",[1238,2696,2698],{"class":1240,"line":2697},160,[1238,2699,2700],{},"        b = model.state_dict()[f\"{prefix}.bias\"].numpy().astype(np.float32)\n",[1238,2702,2704],{"class":1240,"line":2703},161,[1238,2705,2706],{},"        inits += [numpy_helper.from_array(w, name=f\"w_{tag}\"),\n",[1238,2708,2710],{"class":1240,"line":2709},162,[1238,2711,2712],{},"                  numpy_helper.from_array(b, name=f\"b_{tag}\")]\n",[1238,2714,2716],{"class":1240,"line":2715},163,[1238,2717,2718],{},"        mm = f\"mm_{tag}\"; add = f\"add_{tag}\"\n",[1238,2720,2722],{"class":1240,"line":2721},164,[1238,2723,2724],{},"        nodes += [helper.make_node(\"MatMul\", [cur, f\"w_{tag}\"], [mm]),\n",[1238,2726,2728],{"class":1240,"line":2727},165,[1238,2729,2730],{},"                  helper.make_node(\"Add\",    [mm,  f\"b_{tag}\"], [add])]\n",[1238,2732,2734],{"class":1240,"line":2733},166,[1238,2735,2736],{},"        cur = add\n",[1238,2738,2740],{"class":1240,"line":2739},167,[1238,2741,2742],{},"        if relu:\n",[1238,2744,2746],{"class":1240,"line":2745},168,[1238,2747,2748],{},"            r = f\"relu_{tag}\"\n",[1238,2750,2752],{"class":1240,"line":2751},169,[1238,2753,2754],{},"            nodes.append(helper.make_node(\"Relu\", [cur], [r]))\n",[1238,2756,2758],{"class":1240,"line":2757},170,[1238,2759,2760],{},"            cur = r\n",[1238,2762,2764],{"class":1240,"line":2763},171,[1238,2765,1789],{"emptyLinePlaceholder":216},[1238,2767,2769],{"class":1240,"line":2768},172,[1238,2770,2771],{},"    graph = helper.make_graph(\n",[1238,2773,2775],{"class":1240,"line":2774},173,[1238,2776,2777],{},"        nodes, \"autoencoder\",\n",[1238,2779,2781],{"class":1240,"line":2780},174,[1238,2782,2783],{},"        [helper.make_tensor_value_info(\"features\", TensorProto.FLOAT, [None, N])],\n",[1238,2785,2787],{"class":1240,"line":2786},175,[1238,2788,2789],{},"        [helper.make_tensor_value_info(cur, TensorProto.FLOAT, [None, N])],\n",[1238,2791,2793],{"class":1240,"line":2792},176,[1238,2794,2795],{},"        initializer=inits,\n",[1238,2797,2799],{"class":1240,"line":2798},177,[1238,2800,2801],{},"    )\n",[1238,2803,2805],{"class":1240,"line":2804},178,[1238,2806,2807],{},"    proto = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 11)])\n",[1238,2809,2811],{"class":1240,"line":2810},179,[1238,2812,2813],{},"    proto.ir_version = 7\n",[1238,2815,2817],{"class":1240,"line":2816},180,[1238,2818,2819],{},"    onnx.checker.check_model(proto)\n",[1238,2821,2823],{"class":1240,"line":2822},181,[1238,2824,2825],{},"    onnx.save(proto, \"motor_autoencoder.onnx\")\n",[1238,2827,2829],{"class":1240,"line":2828},182,[1238,2830,2831],{},"    print(\"  Exported motor_autoencoder.onnx\")\n",[1238,2833,2835],{"class":1240,"line":2834},183,[1238,2836,1789],{"emptyLinePlaceholder":216},[1238,2838,2840],{"class":1240,"line":2839},184,[1238,2841,2842],{},"    # Sanity check\n",[1238,2844,2846],{"class":1240,"line":2845},185,[1238,2847,2848],{},"    sess = ort.InferenceSession(\"motor_autoencoder.onnx\")\n",[1238,2850,2852],{"class":1240,"line":2851},186,[1238,2853,2854],{},"    out  = sess.run(None, {\"features\": X_norm[:5].astype(np.float32)})[0]\n",[1238,2856,2858],{"class":1240,"line":2857},187,[1238,2859,2860],{},"    mse  = float(np.mean((out - X_norm[:5]) ** 2))\n",[1238,2862,2864],{"class":1240,"line":2863},188,[1238,2865,2866],{},"    print(f\"\\n  Sanity MSE (5 normal samples): {mse:.6f}  threshold: {thresh:.6f}\")\n",[1238,2868,2870],{"class":1240,"line":2869},189,[1238,2871,2872],{},"    if mse \u003C thresh:\n",[1238,2874,2876],{"class":1240,"line":2875},190,[1238,2877,2878],{},"        print(\"  ✓ Model correct , normal data scores below threshold.\")\n",[1238,2880,2882],{"class":1240,"line":2881},191,[1238,2883,2192],{},[1238,2885,2887],{"class":1240,"line":2886},192,[1238,2888,2889],{},"        print(\"  ⚠ Sanity MSE above threshold , collect more data and retrain.\")\n",[1238,2891,2893],{"class":1240,"line":2892},193,[1238,2894,1789],{"emptyLinePlaceholder":216},[1238,2896,2898],{"class":1240,"line":2897},194,[1238,2899,2900],{},"    print(\"\\nDone. Copy these 3 files to your Node-RED server:\")\n",[1238,2902,2904],{"class":1240,"line":2903},195,[1238,2905,2906],{},"    print(\"  motor_autoencoder.onnx  scaler_params.json  threshold.json\")\n",[1238,2908,2910],{"class":1240,"line":2909},196,[1238,2911,1789],{"emptyLinePlaceholder":216},[1238,2913,2915],{"class":1240,"line":2914},197,[1238,2916,2917],{},"def handle_sigint(sig, frame):\n",[1238,2919,2921],{"class":1240,"line":2920},198,[1238,2922,2923],{},"    stop_flag[0] = True\n",[1238,2925,2927],{"class":1240,"line":2926},199,[1238,2928,2929],{},"    if len(training_data) \u003C MIN_WINDOWS:\n",[1238,2931,2933],{"class":1240,"line":2932},200,[1238,2934,2935],{},"        print(f\"\\n\\nNeed at least {MIN_WINDOWS} windows. Restart and collect longer.\")\n",[1238,2937,2939],{"class":1240,"line":2938},201,[1238,2940,2941],{},"        sys.exit(1)\n",[1238,2943,2945],{"class":1240,"line":2944},202,[1238,2946,2947],{},"    train_and_export()\n",[1238,2949,2951],{"class":1240,"line":2950},203,[1238,2952,2953],{},"    sys.exit(0)\n",[1238,2955,2957],{"class":1240,"line":2956},204,[1238,2958,1789],{"emptyLinePlaceholder":216},[1238,2960,2962],{"class":1240,"line":2961},205,[1238,2963,2964],{},"signal.signal(signal.SIGINT, handle_sigint)\n",[1238,2966,2968],{"class":1240,"line":2967},206,[1238,2969,1789],{"emptyLinePlaceholder":216},[1238,2971,2973],{"class":1240,"line":2972},207,[1238,2974,2975],{},"client = mqtt.Client(callback_api_version=CallbackAPIVersion.VERSION2,\n",[1238,2977,2979],{"class":1240,"line":2978},208,[1238,2980,2981],{},"                     client_id=CLIENT_ID)\n",[1238,2983,2985],{"class":1240,"line":2984},209,[1238,2986,2987],{},"client.username_pw_set(USERNAME, PASSWORD)\n",[1238,2989,2991],{"class":1240,"line":2990},210,[1238,2992,2993],{},"client.on_connect = on_connect\n",[1238,2995,2997],{"class":1240,"line":2996},211,[1238,2998,2999],{},"client.on_message = on_message\n",[1238,3001,3003],{"class":1240,"line":3002},212,[1238,3004,3005],{},"client.connect(BROKER, PORT, keepalive=60)\n",[1238,3007,3009],{"class":1240,"line":3008},213,[1238,3010,3011],{},"client.loop_forever()\n",[15,3013,3014,3015,3018],{},"Let it collect for 5–10 minutes (aim for 300+ windows), then press ",[53,3016,3017],{},"Ctrl+C once"," and wait. The script will train the model and export three files:",[47,3020,3021,3027,3033],{},[50,3022,3023,3026],{},[76,3024,3025],{},"motor_autoencoder.onnx"," , the trained model in a portable, runtime-agnostic format",[50,3028,3029,3032],{},[76,3030,3031],{},"scaler_params.json"," , the scaling parameters used to normalise input features",[50,3034,3035,3037],{},[76,3036,1747],{}," , the reconstruction error value above which a reading is flagged as anomalous",[118,3039,3040],{},[15,3041,3042,3045,3046,3049],{},[53,3043,3044],{},"Sanity check:"," Watch the output at the end. ",[76,3047,3048],{},"✓ Model correct"," means the model correctly scores normal data below the threshold. A warning means you should collect more data with the motor under its typical load and retrain.",[143,3051,3053],{"id":3052},"when-to-retrain","When to Retrain",[15,3055,3056],{},"The model captures what normal looks like at the time of training. Plan to retrain after any significant change to the motor's operating conditions: a maintenance overhaul, a change in load profile, a new mounting position, or seasonal temperature shifts that affect the vibration baseline. The process is identical, run the script again with the motor under its new normal conditions, replace the three output files, and restart the Node-RED flow.",[39,3058,3060],{"id":3059},"part-3-deploying-in-node-red","Part 3: Deploying in Node-RED",[15,3062,3063],{},"The model now knows what healthy looks like. This section builds the Node-RED flow that runs continuously, scores every incoming vibration batch in real time, and raises an alert the moment something shifts.",[143,3065,3067],{"id":3066},"installing-the-ai-nodes","Installing the AI Nodes",[15,3069,3070],{},"FlowFuse provides a dedicated AI nodes package for Node-RED that includes ONNX runtime support.",[118,3072,3073],{},[15,3074,3075,3078,3079,474],{},[53,3076,3077],{},"Note:"," These nodes are only available to FlowFuse users. If you don't have an account, [get started here]({% include \"sign-up-url.njk\" %}) and follow the steps to ",[22,3080,3082],{"href":3081},"\u002Fblog\u002F2025\u002F09\u002Finstalling-node-red\u002F","run the device agent",[3084,3085,3086,3092,3098],"ol",{},[50,3087,3088,3089],{},"Open the Node-RED editor and go to ",[53,3090,3091],{},"Menu → Manage Palette",[50,3093,3094,3095],{},"Search for ",[76,3096,3097],{},"@flowfuse-nodes\u002Fnr-ai-nodes",[50,3099,3100,3101],{},"Click ",[53,3102,3103],{},"Install",[15,3105,3106,3107,3110],{},"Once installed, you will see new nodes in the palette under the FlowFuse AI category. This guide uses the ",[53,3108,3109],{},"onnx"," node.",[15,3112,3113],{},[30,3114],{"alt":3115,"src":3116,"title":3115},"FlowFuse AI nodes visible in the Node-RED palette under the FlowFuse AI category","\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fai-nodes.png",[143,3118,3120],{"id":3119},"loading-the-model-files","Loading the Model Files",[15,3122,3123],{},"Place your three model files in the FlowFuse Device Agent directory before building the flow:",[1195,3125,3127],{"className":1488,"code":3126,"language":1490,"meta":187,"style":187},"sudo mkdir -p \u002Fopt\u002Fflowfuse-device\u002Fmodels\nsudo cp motor_autoencoder.onnx \u002Fopt\u002Fflowfuse-device\u002Fmodels\u002F\nsudo cp scaler_params.json \u002Fopt\u002Fflowfuse-device\u002Fmodels\u002F\nsudo cp threshold.json \u002Fopt\u002Fflowfuse-device\u002Fmodels\u002F\n",[76,3128,3129,3143,3156,3167],{"__ignoreMap":187},[1238,3130,3131,3134,3137,3140],{"class":1240,"line":1241},[1238,3132,3133],{"class":1497},"sudo",[1238,3135,3136],{"class":1266}," mkdir",[1238,3138,3139],{"class":1266}," -p",[1238,3141,3142],{"class":1266}," \u002Fopt\u002Fflowfuse-device\u002Fmodels\n",[1238,3144,3145,3147,3150,3153],{"class":1240,"line":191},[1238,3146,3133],{"class":1497},[1238,3148,3149],{"class":1266}," cp",[1238,3151,3152],{"class":1266}," motor_autoencoder.onnx",[1238,3154,3155],{"class":1266}," \u002Fopt\u002Fflowfuse-device\u002Fmodels\u002F\n",[1238,3157,3158,3160,3162,3165],{"class":1240,"line":196},[1238,3159,3133],{"class":1497},[1238,3161,3149],{"class":1266},[1238,3163,3164],{"class":1266}," scaler_params.json",[1238,3166,3155],{"class":1266},[1238,3168,3169,3171,3173,3176],{"class":1240,"line":188},[1238,3170,3133],{"class":1497},[1238,3172,3149],{"class":1266},[1238,3174,3175],{"class":1266}," threshold.json",[1238,3177,3155],{"class":1266},[143,3179,3181],{"id":3180},"building-the-inference-flow","Building the Inference Flow",[15,3183,3184],{},"The flow has five stages: receive the payload, extract features, scale and prepare, run inference, and score the result.",[15,3186,3187],{},[53,3188,3189],{},"1. Subscribe to MQTT",[15,3191,3192,3193,3196],{},"Add an ",[53,3194,3195],{},"mqtt-in"," node and configure it to connect to the same broker and topic used during training. Set the output to auto-detect so the JSON payload is parsed automatically.",[15,3198,3199,3200,3204],{},"If you are using the built-in FlowFuse MQTT broker, use the ",[22,3201,3203],{"href":3202},"\u002Fnode-red\u002Fflowfuse\u002Fmqtt\u002F","FlowFuse MQTT nodes"," , these connect automatically when dragged into the flow.",[15,3206,3207],{},[53,3208,3209],{},"2. Extract Features",[15,3211,3212,3213,3216,3217,3220,3221,3224,3225,3228],{},"Add a ",[53,3214,3215],{},"function"," node. In the ",[53,3218,3219],{},"Setup"," tab, add the module ",[76,3222,3223],{},"fs",". Then paste the following into the ",[53,3226,3227],{},"On Message"," tab:",[1195,3230,3234],{"className":3231,"code":3232,"language":3233,"meta":187,"style":187},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","function extractFeatures(sig) {\n    const arr = sig.map(Number);\n    const n = arr.length;\n    const mean = arr.reduce((a, b) => a + b, 0) \u002F n;\n    const std = Math.sqrt(arr.reduce((a, b) => a + (b - mean) ** 2, 0) \u002F n) + 1e-9;\n    const absArr = arr.map(Math.abs);\n    const rms = Math.sqrt(arr.reduce((a, b) => a + b * b, 0) \u002F n);\n    const peak = Math.max(...absArr);\n    const meanAbs = absArr.reduce((a, b) => a + b, 0) \u002F n + 1e-9;\n    const meanSqrtAbs = absArr.reduce((a, b) => a + Math.sqrt(b), 0) \u002F n;\n    return [\n        rms, peak, Math.max(...arr) - Math.min(...arr),\n        peak \u002F (rms + 1e-9),\n        arr.reduce((a, b) => a + (b - mean) ** 2, 0) \u002F n,\n        Math.sqrt(arr.reduce((a, b) => a + (b - mean) ** 2, 0) \u002F n),\n        arr.reduce((a, b) => a + ((b - mean) \u002F std) ** 3, 0) \u002F n,\n        arr.reduce((a, b) => a + ((b - mean) \u002F std) ** 4, 0) \u002F n - 3,\n        rms \u002F meanAbs, peak \u002F meanAbs, peak \u002F (meanSqrtAbs ** 2 + 1e-9),\n    ];\n}\n\n\u002F\u002F Scaler and threshold are cached in flow context after the first message.\n\u002F\u002F If you update the model files, restart the Node-RED flow to reload them.\nif (!flow.get('scaler')) {\n    const sc = JSON.parse(fs.readFileSync('\u002Fopt\u002Fflowfuse-device\u002Fmodels\u002Fscaler_params.json'));\n    const th = JSON.parse(fs.readFileSync('\u002Fopt\u002Fflowfuse-device\u002Fmodels\u002Fthreshold.json'));\n    flow.set('scaler', sc);\n    flow.set('threshold', th.threshold);\n}\n\nconst scaler = flow.get('scaler');\nconst CLIP = scaler.clip || 5.0;\nconst MIN_STD = 0.1;\n\nconst raw = [\n    ...extractFeatures(msg.payload.x),\n    ...extractFeatures(msg.payload.y),\n    ...extractFeatures(msg.payload.z)\n];\n\nconst normalised = raw.map((v, i) => {\n    const s = Math.max(scaler.std[i], MIN_STD);\n    const n = (v - scaler.mean[i]) \u002F s;\n    return Math.max(-CLIP, Math.min(CLIP, n));\n});\n\nmsg.input = {\n    data: new Float32Array(normalised),\n    type: \"float32\",\n    dims: [1, 33]\n};\nmsg.payload = msg.input;\nmsg.threshold = flow.get('threshold');\nreturn msg;\n","javascript",[76,3235,3236,3256,3286,3304,3358,3441,3470,3532,3560,3611,3668,3677,3722,3743,3796,3859,3919,3982,4022,4029,4033,4037,4043,4048,4081,4121,4157,4183,4212,4216,4220,4250,4275,4289,4293,4304,4327,4346,4363,4370,4374,4408,4449,4483,4519,4528,4532,4545,4563,4579,4598,4603,4624,4653],{"__ignoreMap":187},[1238,3237,3238,3240,3243,3246,3250,3253],{"class":1240,"line":1241},[1238,3239,3215],{"class":1253},[1238,3241,3242],{"class":1512}," extractFeatures",[1238,3244,3245],{"class":1244},"(",[1238,3247,3249],{"class":3248},"sHdIc","sig",[1238,3251,3252],{"class":1244},")",[1238,3254,3255],{"class":1244}," {\n",[1238,3257,3258,3261,3264,3267,3270,3272,3275,3278,3281,3283],{"class":1240,"line":191},[1238,3259,3260],{"class":1253},"    const",[1238,3262,3263],{"class":1327}," arr",[1238,3265,3266],{"class":1244}," =",[1238,3268,3269],{"class":1327}," sig",[1238,3271,474],{"class":1244},[1238,3273,3274],{"class":1512},"map",[1238,3276,3245],{"class":3277},"swJcz",[1238,3279,3280],{"class":1327},"Number",[1238,3282,3252],{"class":3277},[1238,3284,3285],{"class":1244},";\n",[1238,3287,3288,3290,3293,3295,3297,3299,3302],{"class":1240,"line":196},[1238,3289,3260],{"class":1253},[1238,3291,3292],{"class":1327}," n",[1238,3294,3266],{"class":1244},[1238,3296,3263],{"class":1327},[1238,3298,474],{"class":1244},[1238,3300,3301],{"class":1327},"length",[1238,3303,3285],{"class":1244},[1238,3305,3306,3308,3311,3313,3315,3317,3320,3322,3324,3326,3328,3331,3333,3336,3339,3342,3344,3346,3349,3352,3354,3356],{"class":1240,"line":188},[1238,3307,3260],{"class":1253},[1238,3309,3310],{"class":1327}," mean",[1238,3312,3266],{"class":1244},[1238,3314,3263],{"class":1327},[1238,3316,474],{"class":1244},[1238,3318,3319],{"class":1512},"reduce",[1238,3321,3245],{"class":3277},[1238,3323,3245],{"class":1244},[1238,3325,22],{"class":3248},[1238,3327,1309],{"class":1244},[1238,3329,3330],{"class":3248}," b",[1238,3332,3252],{"class":1244},[1238,3334,3335],{"class":1253}," =>",[1238,3337,3338],{"class":1327}," a",[1238,3340,3341],{"class":1244}," +",[1238,3343,3330],{"class":1327},[1238,3345,1309],{"class":1244},[1238,3347,3348],{"class":1286}," 0",[1238,3350,3351],{"class":3277},") ",[1238,3353,1452],{"class":1244},[1238,3355,3292],{"class":1327},[1238,3357,3285],{"class":1244},[1238,3359,3360,3362,3365,3367,3370,3372,3375,3377,3380,3382,3384,3386,3388,3390,3392,3394,3396,3398,3400,3402,3405,3408,3411,3413,3415,3418,3421,3423,3425,3427,3429,3431,3433,3436,3439],{"class":1240,"line":1334},[1238,3361,3260],{"class":1253},[1238,3363,3364],{"class":1327}," std",[1238,3366,3266],{"class":1244},[1238,3368,3369],{"class":1327}," Math",[1238,3371,474],{"class":1244},[1238,3373,3374],{"class":1512},"sqrt",[1238,3376,3245],{"class":3277},[1238,3378,3379],{"class":1327},"arr",[1238,3381,474],{"class":1244},[1238,3383,3319],{"class":1512},[1238,3385,3245],{"class":3277},[1238,3387,3245],{"class":1244},[1238,3389,22],{"class":3248},[1238,3391,1309],{"class":1244},[1238,3393,3330],{"class":3248},[1238,3395,3252],{"class":1244},[1238,3397,3335],{"class":1253},[1238,3399,3338],{"class":1327},[1238,3401,3341],{"class":1244},[1238,3403,3404],{"class":3277}," (",[1238,3406,3407],{"class":1327},"b",[1238,3409,3410],{"class":1244}," -",[1238,3412,3310],{"class":1327},[1238,3414,3351],{"class":3277},[1238,3416,3417],{"class":1244},"**",[1238,3419,3420],{"class":1286}," 2",[1238,3422,1309],{"class":1244},[1238,3424,3348],{"class":1286},[1238,3426,3351],{"class":3277},[1238,3428,1452],{"class":1244},[1238,3430,3292],{"class":1327},[1238,3432,3351],{"class":3277},[1238,3434,3435],{"class":1244},"+",[1238,3437,3438],{"class":1286}," 1e-9",[1238,3440,3285],{"class":1244},[1238,3442,3443,3445,3448,3450,3452,3454,3456,3458,3461,3463,3466,3468],{"class":1240,"line":1372},[1238,3444,3260],{"class":1253},[1238,3446,3447],{"class":1327}," absArr",[1238,3449,3266],{"class":1244},[1238,3451,3263],{"class":1327},[1238,3453,474],{"class":1244},[1238,3455,3274],{"class":1512},[1238,3457,3245],{"class":3277},[1238,3459,3460],{"class":1327},"Math",[1238,3462,474],{"class":1244},[1238,3464,3465],{"class":1327},"abs",[1238,3467,3252],{"class":3277},[1238,3469,3285],{"class":1244},[1238,3471,3472,3474,3477,3479,3481,3483,3485,3487,3489,3491,3493,3495,3497,3499,3501,3503,3505,3507,3509,3511,3513,3516,3518,3520,3522,3524,3526,3528,3530],{"class":1240,"line":1411},[1238,3473,3260],{"class":1253},[1238,3475,3476],{"class":1327}," rms",[1238,3478,3266],{"class":1244},[1238,3480,3369],{"class":1327},[1238,3482,474],{"class":1244},[1238,3484,3374],{"class":1512},[1238,3486,3245],{"class":3277},[1238,3488,3379],{"class":1327},[1238,3490,474],{"class":1244},[1238,3492,3319],{"class":1512},[1238,3494,3245],{"class":3277},[1238,3496,3245],{"class":1244},[1238,3498,22],{"class":3248},[1238,3500,1309],{"class":1244},[1238,3502,3330],{"class":3248},[1238,3504,3252],{"class":1244},[1238,3506,3335],{"class":1253},[1238,3508,3338],{"class":1327},[1238,3510,3341],{"class":1244},[1238,3512,3330],{"class":1327},[1238,3514,3515],{"class":1244}," *",[1238,3517,3330],{"class":1327},[1238,3519,1309],{"class":1244},[1238,3521,3348],{"class":1286},[1238,3523,3351],{"class":3277},[1238,3525,1452],{"class":1244},[1238,3527,3292],{"class":1327},[1238,3529,3252],{"class":3277},[1238,3531,3285],{"class":1244},[1238,3533,3534,3536,3539,3541,3543,3545,3548,3550,3553,3556,3558],{"class":1240,"line":1807},[1238,3535,3260],{"class":1253},[1238,3537,3538],{"class":1327}," peak",[1238,3540,3266],{"class":1244},[1238,3542,3369],{"class":1327},[1238,3544,474],{"class":1244},[1238,3546,3547],{"class":1512},"max",[1238,3549,3245],{"class":3277},[1238,3551,3552],{"class":1244},"...",[1238,3554,3555],{"class":1327},"absArr",[1238,3557,3252],{"class":3277},[1238,3559,3285],{"class":1244},[1238,3561,3562,3564,3567,3569,3571,3573,3575,3577,3579,3581,3583,3585,3587,3589,3591,3593,3595,3597,3599,3601,3603,3605,3607,3609],{"class":1240,"line":1813},[1238,3563,3260],{"class":1253},[1238,3565,3566],{"class":1327}," meanAbs",[1238,3568,3266],{"class":1244},[1238,3570,3447],{"class":1327},[1238,3572,474],{"class":1244},[1238,3574,3319],{"class":1512},[1238,3576,3245],{"class":3277},[1238,3578,3245],{"class":1244},[1238,3580,22],{"class":3248},[1238,3582,1309],{"class":1244},[1238,3584,3330],{"class":3248},[1238,3586,3252],{"class":1244},[1238,3588,3335],{"class":1253},[1238,3590,3338],{"class":1327},[1238,3592,3341],{"class":1244},[1238,3594,3330],{"class":1327},[1238,3596,1309],{"class":1244},[1238,3598,3348],{"class":1286},[1238,3600,3351],{"class":3277},[1238,3602,1452],{"class":1244},[1238,3604,3292],{"class":1327},[1238,3606,3341],{"class":1244},[1238,3608,3438],{"class":1286},[1238,3610,3285],{"class":1244},[1238,3612,3613,3615,3618,3620,3622,3624,3626,3628,3630,3632,3634,3636,3638,3640,3642,3644,3646,3648,3650,3652,3654,3656,3658,3660,3662,3664,3666],{"class":1240,"line":1819},[1238,3614,3260],{"class":1253},[1238,3616,3617],{"class":1327}," meanSqrtAbs",[1238,3619,3266],{"class":1244},[1238,3621,3447],{"class":1327},[1238,3623,474],{"class":1244},[1238,3625,3319],{"class":1512},[1238,3627,3245],{"class":3277},[1238,3629,3245],{"class":1244},[1238,3631,22],{"class":3248},[1238,3633,1309],{"class":1244},[1238,3635,3330],{"class":3248},[1238,3637,3252],{"class":1244},[1238,3639,3335],{"class":1253},[1238,3641,3338],{"class":1327},[1238,3643,3341],{"class":1244},[1238,3645,3369],{"class":1327},[1238,3647,474],{"class":1244},[1238,3649,3374],{"class":1512},[1238,3651,3245],{"class":3277},[1238,3653,3407],{"class":1327},[1238,3655,3252],{"class":3277},[1238,3657,1309],{"class":1244},[1238,3659,3348],{"class":1286},[1238,3661,3351],{"class":3277},[1238,3663,1452],{"class":1244},[1238,3665,3292],{"class":1327},[1238,3667,3285],{"class":1244},[1238,3669,3670,3674],{"class":1240,"line":1825},[1238,3671,3673],{"class":3672},"s7zQu","    return",[1238,3675,3676],{"class":3277}," [\n",[1238,3678,3679,3682,3684,3686,3688,3690,3692,3694,3696,3698,3700,3702,3705,3707,3709,3712,3714,3716,3718,3720],{"class":1240,"line":1831},[1238,3680,3681],{"class":1327},"        rms",[1238,3683,1309],{"class":1244},[1238,3685,3538],{"class":1327},[1238,3687,1309],{"class":1244},[1238,3689,3369],{"class":1327},[1238,3691,474],{"class":1244},[1238,3693,3547],{"class":1512},[1238,3695,3245],{"class":3277},[1238,3697,3552],{"class":1244},[1238,3699,3379],{"class":1327},[1238,3701,3351],{"class":3277},[1238,3703,3704],{"class":1244},"-",[1238,3706,3369],{"class":1327},[1238,3708,474],{"class":1244},[1238,3710,3711],{"class":1512},"min",[1238,3713,3245],{"class":3277},[1238,3715,3552],{"class":1244},[1238,3717,3379],{"class":1327},[1238,3719,3252],{"class":3277},[1238,3721,1272],{"class":1244},[1238,3723,3724,3727,3730,3732,3735,3737,3739,3741],{"class":1240,"line":1837},[1238,3725,3726],{"class":1327},"        peak",[1238,3728,3729],{"class":1244}," \u002F",[1238,3731,3404],{"class":3277},[1238,3733,3734],{"class":1327},"rms",[1238,3736,3341],{"class":1244},[1238,3738,3438],{"class":1286},[1238,3740,3252],{"class":3277},[1238,3742,1272],{"class":1244},[1238,3744,3745,3748,3750,3752,3754,3756,3758,3760,3762,3764,3766,3768,3770,3772,3774,3776,3778,3780,3782,3784,3786,3788,3790,3792,3794],{"class":1240,"line":1843},[1238,3746,3747],{"class":1327},"        arr",[1238,3749,474],{"class":1244},[1238,3751,3319],{"class":1512},[1238,3753,3245],{"class":3277},[1238,3755,3245],{"class":1244},[1238,3757,22],{"class":3248},[1238,3759,1309],{"class":1244},[1238,3761,3330],{"class":3248},[1238,3763,3252],{"class":1244},[1238,3765,3335],{"class":1253},[1238,3767,3338],{"class":1327},[1238,3769,3341],{"class":1244},[1238,3771,3404],{"class":3277},[1238,3773,3407],{"class":1327},[1238,3775,3410],{"class":1244},[1238,3777,3310],{"class":1327},[1238,3779,3351],{"class":3277},[1238,3781,3417],{"class":1244},[1238,3783,3420],{"class":1286},[1238,3785,1309],{"class":1244},[1238,3787,3348],{"class":1286},[1238,3789,3351],{"class":3277},[1238,3791,1452],{"class":1244},[1238,3793,3292],{"class":1327},[1238,3795,1272],{"class":1244},[1238,3797,3798,3801,3803,3805,3807,3809,3811,3813,3815,3817,3819,3821,3823,3825,3827,3829,3831,3833,3835,3837,3839,3841,3843,3845,3847,3849,3851,3853,3855,3857],{"class":1240,"line":1849},[1238,3799,3800],{"class":1327},"        Math",[1238,3802,474],{"class":1244},[1238,3804,3374],{"class":1512},[1238,3806,3245],{"class":3277},[1238,3808,3379],{"class":1327},[1238,3810,474],{"class":1244},[1238,3812,3319],{"class":1512},[1238,3814,3245],{"class":3277},[1238,3816,3245],{"class":1244},[1238,3818,22],{"class":3248},[1238,3820,1309],{"class":1244},[1238,3822,3330],{"class":3248},[1238,3824,3252],{"class":1244},[1238,3826,3335],{"class":1253},[1238,3828,3338],{"class":1327},[1238,3830,3341],{"class":1244},[1238,3832,3404],{"class":3277},[1238,3834,3407],{"class":1327},[1238,3836,3410],{"class":1244},[1238,3838,3310],{"class":1327},[1238,3840,3351],{"class":3277},[1238,3842,3417],{"class":1244},[1238,3844,3420],{"class":1286},[1238,3846,1309],{"class":1244},[1238,3848,3348],{"class":1286},[1238,3850,3351],{"class":3277},[1238,3852,1452],{"class":1244},[1238,3854,3292],{"class":1327},[1238,3856,3252],{"class":3277},[1238,3858,1272],{"class":1244},[1238,3860,3861,3863,3865,3867,3869,3871,3873,3875,3877,3879,3881,3883,3885,3888,3890,3892,3894,3896,3898,3900,3902,3904,3907,3909,3911,3913,3915,3917],{"class":1240,"line":1855},[1238,3862,3747],{"class":1327},[1238,3864,474],{"class":1244},[1238,3866,3319],{"class":1512},[1238,3868,3245],{"class":3277},[1238,3870,3245],{"class":1244},[1238,3872,22],{"class":3248},[1238,3874,1309],{"class":1244},[1238,3876,3330],{"class":3248},[1238,3878,3252],{"class":1244},[1238,3880,3335],{"class":1253},[1238,3882,3338],{"class":1327},[1238,3884,3341],{"class":1244},[1238,3886,3887],{"class":3277}," ((",[1238,3889,3407],{"class":1327},[1238,3891,3410],{"class":1244},[1238,3893,3310],{"class":1327},[1238,3895,3351],{"class":3277},[1238,3897,1452],{"class":1244},[1238,3899,3364],{"class":1327},[1238,3901,3351],{"class":3277},[1238,3903,3417],{"class":1244},[1238,3905,3906],{"class":1286}," 3",[1238,3908,1309],{"class":1244},[1238,3910,3348],{"class":1286},[1238,3912,3351],{"class":3277},[1238,3914,1452],{"class":1244},[1238,3916,3292],{"class":1327},[1238,3918,1272],{"class":1244},[1238,3920,3921,3923,3925,3927,3929,3931,3933,3935,3937,3939,3941,3943,3945,3947,3949,3951,3953,3955,3957,3959,3961,3963,3966,3968,3970,3972,3974,3976,3978,3980],{"class":1240,"line":1860},[1238,3922,3747],{"class":1327},[1238,3924,474],{"class":1244},[1238,3926,3319],{"class":1512},[1238,3928,3245],{"class":3277},[1238,3930,3245],{"class":1244},[1238,3932,22],{"class":3248},[1238,3934,1309],{"class":1244},[1238,3936,3330],{"class":3248},[1238,3938,3252],{"class":1244},[1238,3940,3335],{"class":1253},[1238,3942,3338],{"class":1327},[1238,3944,3341],{"class":1244},[1238,3946,3887],{"class":3277},[1238,3948,3407],{"class":1327},[1238,3950,3410],{"class":1244},[1238,3952,3310],{"class":1327},[1238,3954,3351],{"class":3277},[1238,3956,1452],{"class":1244},[1238,3958,3364],{"class":1327},[1238,3960,3351],{"class":3277},[1238,3962,3417],{"class":1244},[1238,3964,3965],{"class":1286}," 4",[1238,3967,1309],{"class":1244},[1238,3969,3348],{"class":1286},[1238,3971,3351],{"class":3277},[1238,3973,1452],{"class":1244},[1238,3975,3292],{"class":1327},[1238,3977,3410],{"class":1244},[1238,3979,3906],{"class":1286},[1238,3981,1272],{"class":1244},[1238,3983,3984,3986,3988,3990,3992,3994,3996,3998,4000,4002,4004,4006,4009,4012,4014,4016,4018,4020],{"class":1240,"line":1866},[1238,3985,3681],{"class":1327},[1238,3987,3729],{"class":1244},[1238,3989,3566],{"class":1327},[1238,3991,1309],{"class":1244},[1238,3993,3538],{"class":1327},[1238,3995,3729],{"class":1244},[1238,3997,3566],{"class":1327},[1238,3999,1309],{"class":1244},[1238,4001,3538],{"class":1327},[1238,4003,3729],{"class":1244},[1238,4005,3404],{"class":3277},[1238,4007,4008],{"class":1327},"meanSqrtAbs",[1238,4010,4011],{"class":1244}," **",[1238,4013,3420],{"class":1286},[1238,4015,3341],{"class":1244},[1238,4017,3438],{"class":1286},[1238,4019,3252],{"class":3277},[1238,4021,1272],{"class":1244},[1238,4023,4024,4027],{"class":1240,"line":1872},[1238,4025,4026],{"class":3277},"    ]",[1238,4028,3285],{"class":1244},[1238,4030,4031],{"class":1240,"line":1878},[1238,4032,1414],{"class":1244},[1238,4034,4035],{"class":1240,"line":1884},[1238,4036,1789],{"emptyLinePlaceholder":216},[1238,4038,4039],{"class":1240,"line":1890},[1238,4040,4042],{"class":4041},"sHwdD","\u002F\u002F Scaler and threshold are cached in flow context after the first message.\n",[1238,4044,4045],{"class":1240,"line":1896},[1238,4046,4047],{"class":4041},"\u002F\u002F If you update the model files, restart the Node-RED flow to reload them.\n",[1238,4049,4050,4053,4055,4058,4061,4063,4066,4068,4071,4074,4076,4079],{"class":1240,"line":1902},[1238,4051,4052],{"class":3672},"if",[1238,4054,3404],{"class":1327},[1238,4056,4057],{"class":1244},"!",[1238,4059,4060],{"class":1327},"flow",[1238,4062,474],{"class":1244},[1238,4064,4065],{"class":1512},"get",[1238,4067,3245],{"class":1327},[1238,4069,4070],{"class":1244},"'",[1238,4072,4073],{"class":1266},"scaler",[1238,4075,4070],{"class":1244},[1238,4077,4078],{"class":1327},")) ",[1238,4080,1245],{"class":1244},[1238,4082,4083,4085,4088,4090,4093,4095,4098,4100,4102,4104,4107,4109,4111,4114,4116,4119],{"class":1240,"line":1907},[1238,4084,3260],{"class":1253},[1238,4086,4087],{"class":1327}," sc",[1238,4089,3266],{"class":1244},[1238,4091,4092],{"class":1327}," JSON",[1238,4094,474],{"class":1244},[1238,4096,4097],{"class":1512},"parse",[1238,4099,3245],{"class":3277},[1238,4101,3223],{"class":1327},[1238,4103,474],{"class":1244},[1238,4105,4106],{"class":1512},"readFileSync",[1238,4108,3245],{"class":3277},[1238,4110,4070],{"class":1244},[1238,4112,4113],{"class":1266},"\u002Fopt\u002Fflowfuse-device\u002Fmodels\u002Fscaler_params.json",[1238,4115,4070],{"class":1244},[1238,4117,4118],{"class":3277},"))",[1238,4120,3285],{"class":1244},[1238,4122,4123,4125,4128,4130,4132,4134,4136,4138,4140,4142,4144,4146,4148,4151,4153,4155],{"class":1240,"line":1913},[1238,4124,3260],{"class":1253},[1238,4126,4127],{"class":1327}," th",[1238,4129,3266],{"class":1244},[1238,4131,4092],{"class":1327},[1238,4133,474],{"class":1244},[1238,4135,4097],{"class":1512},[1238,4137,3245],{"class":3277},[1238,4139,3223],{"class":1327},[1238,4141,474],{"class":1244},[1238,4143,4106],{"class":1512},[1238,4145,3245],{"class":3277},[1238,4147,4070],{"class":1244},[1238,4149,4150],{"class":1266},"\u002Fopt\u002Fflowfuse-device\u002Fmodels\u002Fthreshold.json",[1238,4152,4070],{"class":1244},[1238,4154,4118],{"class":3277},[1238,4156,3285],{"class":1244},[1238,4158,4159,4162,4164,4167,4169,4171,4173,4175,4177,4179,4181],{"class":1240,"line":1919},[1238,4160,4161],{"class":1327},"    flow",[1238,4163,474],{"class":1244},[1238,4165,4166],{"class":1512},"set",[1238,4168,3245],{"class":3277},[1238,4170,4070],{"class":1244},[1238,4172,4073],{"class":1266},[1238,4174,4070],{"class":1244},[1238,4176,1309],{"class":1244},[1238,4178,4087],{"class":1327},[1238,4180,3252],{"class":3277},[1238,4182,3285],{"class":1244},[1238,4184,4185,4187,4189,4191,4193,4195,4198,4200,4202,4204,4206,4208,4210],{"class":1240,"line":1925},[1238,4186,4161],{"class":1327},[1238,4188,474],{"class":1244},[1238,4190,4166],{"class":1512},[1238,4192,3245],{"class":3277},[1238,4194,4070],{"class":1244},[1238,4196,4197],{"class":1266},"threshold",[1238,4199,4070],{"class":1244},[1238,4201,1309],{"class":1244},[1238,4203,4127],{"class":1327},[1238,4205,474],{"class":1244},[1238,4207,4197],{"class":1327},[1238,4209,3252],{"class":3277},[1238,4211,3285],{"class":1244},[1238,4213,4214],{"class":1240,"line":1931},[1238,4215,1414],{"class":1244},[1238,4217,4218],{"class":1240,"line":1937},[1238,4219,1789],{"emptyLinePlaceholder":216},[1238,4221,4222,4225,4228,4231,4234,4236,4238,4240,4242,4244,4246,4248],{"class":1240,"line":1943},[1238,4223,4224],{"class":1253},"const",[1238,4226,4227],{"class":1327}," scaler ",[1238,4229,4230],{"class":1244},"=",[1238,4232,4233],{"class":1327}," flow",[1238,4235,474],{"class":1244},[1238,4237,4065],{"class":1512},[1238,4239,3245],{"class":1327},[1238,4241,4070],{"class":1244},[1238,4243,4073],{"class":1266},[1238,4245,4070],{"class":1244},[1238,4247,3252],{"class":1327},[1238,4249,3285],{"class":1244},[1238,4251,4252,4254,4257,4259,4262,4264,4267,4270,4273],{"class":1240,"line":1949},[1238,4253,4224],{"class":1253},[1238,4255,4256],{"class":1327}," CLIP ",[1238,4258,4230],{"class":1244},[1238,4260,4261],{"class":1327}," scaler",[1238,4263,474],{"class":1244},[1238,4265,4266],{"class":1327},"clip ",[1238,4268,4269],{"class":1244},"||",[1238,4271,4272],{"class":1286}," 5.0",[1238,4274,3285],{"class":1244},[1238,4276,4277,4279,4282,4284,4287],{"class":1240,"line":1955},[1238,4278,4224],{"class":1253},[1238,4280,4281],{"class":1327}," MIN_STD ",[1238,4283,4230],{"class":1244},[1238,4285,4286],{"class":1286}," 0.1",[1238,4288,3285],{"class":1244},[1238,4290,4291],{"class":1240,"line":1960},[1238,4292,1789],{"emptyLinePlaceholder":216},[1238,4294,4295,4297,4300,4302],{"class":1240,"line":1966},[1238,4296,4224],{"class":1253},[1238,4298,4299],{"class":1327}," raw ",[1238,4301,4230],{"class":1244},[1238,4303,3676],{"class":1327},[1238,4305,4306,4309,4312,4315,4317,4320,4322,4325],{"class":1240,"line":1972},[1238,4307,4308],{"class":1244},"    ...",[1238,4310,4311],{"class":1512},"extractFeatures",[1238,4313,4314],{"class":1327},"(msg",[1238,4316,474],{"class":1244},[1238,4318,4319],{"class":1327},"payload",[1238,4321,474],{"class":1244},[1238,4323,4324],{"class":1327},"x)",[1238,4326,1272],{"class":1244},[1238,4328,4329,4331,4333,4335,4337,4339,4341,4344],{"class":1240,"line":1977},[1238,4330,4308],{"class":1244},[1238,4332,4311],{"class":1512},[1238,4334,4314],{"class":1327},[1238,4336,474],{"class":1244},[1238,4338,4319],{"class":1327},[1238,4340,474],{"class":1244},[1238,4342,4343],{"class":1327},"y)",[1238,4345,1272],{"class":1244},[1238,4347,4348,4350,4352,4354,4356,4358,4360],{"class":1240,"line":1983},[1238,4349,4308],{"class":1244},[1238,4351,4311],{"class":1512},[1238,4353,4314],{"class":1327},[1238,4355,474],{"class":1244},[1238,4357,4319],{"class":1327},[1238,4359,474],{"class":1244},[1238,4361,4362],{"class":1327},"z)\n",[1238,4364,4365,4368],{"class":1240,"line":1989},[1238,4366,4367],{"class":1327},"]",[1238,4369,3285],{"class":1244},[1238,4371,4372],{"class":1240,"line":1995},[1238,4373,1789],{"emptyLinePlaceholder":216},[1238,4375,4376,4378,4381,4383,4386,4388,4390,4392,4394,4397,4399,4402,4404,4406],{"class":1240,"line":2001},[1238,4377,4224],{"class":1253},[1238,4379,4380],{"class":1327}," normalised ",[1238,4382,4230],{"class":1244},[1238,4384,4385],{"class":1327}," raw",[1238,4387,474],{"class":1244},[1238,4389,3274],{"class":1512},[1238,4391,3245],{"class":1327},[1238,4393,3245],{"class":1244},[1238,4395,4396],{"class":3248},"v",[1238,4398,1309],{"class":1244},[1238,4400,4401],{"class":3248}," i",[1238,4403,3252],{"class":1244},[1238,4405,3335],{"class":1253},[1238,4407,3255],{"class":1244},[1238,4409,4410,4412,4415,4417,4419,4421,4423,4425,4427,4429,4432,4435,4438,4440,4442,4445,4447],{"class":1240,"line":2007},[1238,4411,3260],{"class":1253},[1238,4413,4414],{"class":1327}," s",[1238,4416,3266],{"class":1244},[1238,4418,3369],{"class":1327},[1238,4420,474],{"class":1244},[1238,4422,3547],{"class":1512},[1238,4424,3245],{"class":3277},[1238,4426,4073],{"class":1327},[1238,4428,474],{"class":1244},[1238,4430,4431],{"class":1327},"std",[1238,4433,4434],{"class":3277},"[",[1238,4436,4437],{"class":1327},"i",[1238,4439,4367],{"class":3277},[1238,4441,1309],{"class":1244},[1238,4443,4444],{"class":1327}," MIN_STD",[1238,4446,3252],{"class":3277},[1238,4448,3285],{"class":1244},[1238,4450,4451,4453,4455,4457,4459,4461,4463,4465,4467,4470,4472,4474,4477,4479,4481],{"class":1240,"line":2012},[1238,4452,3260],{"class":1253},[1238,4454,3292],{"class":1327},[1238,4456,3266],{"class":1244},[1238,4458,3404],{"class":3277},[1238,4460,4396],{"class":1327},[1238,4462,3410],{"class":1244},[1238,4464,4261],{"class":1327},[1238,4466,474],{"class":1244},[1238,4468,4469],{"class":1327},"mean",[1238,4471,4434],{"class":3277},[1238,4473,4437],{"class":1327},[1238,4475,4476],{"class":3277},"]) ",[1238,4478,1452],{"class":1244},[1238,4480,4414],{"class":1327},[1238,4482,3285],{"class":1244},[1238,4484,4485,4487,4489,4491,4493,4495,4497,4499,4501,4503,4505,4507,4509,4511,4513,4515,4517],{"class":1240,"line":2018},[1238,4486,3673],{"class":3672},[1238,4488,3369],{"class":1327},[1238,4490,474],{"class":1244},[1238,4492,3547],{"class":1512},[1238,4494,3245],{"class":3277},[1238,4496,3704],{"class":1244},[1238,4498,1692],{"class":1327},[1238,4500,1309],{"class":1244},[1238,4502,3369],{"class":1327},[1238,4504,474],{"class":1244},[1238,4506,3711],{"class":1512},[1238,4508,3245],{"class":3277},[1238,4510,1692],{"class":1327},[1238,4512,1309],{"class":1244},[1238,4514,3292],{"class":1327},[1238,4516,4118],{"class":3277},[1238,4518,3285],{"class":1244},[1238,4520,4521,4524,4526],{"class":1240,"line":2024},[1238,4522,4523],{"class":1244},"}",[1238,4525,3252],{"class":1327},[1238,4527,3285],{"class":1244},[1238,4529,4530],{"class":1240,"line":2030},[1238,4531,1789],{"emptyLinePlaceholder":216},[1238,4533,4534,4536,4538,4541,4543],{"class":1240,"line":2036},[1238,4535,78],{"class":1327},[1238,4537,474],{"class":1244},[1238,4539,4540],{"class":1327},"input ",[1238,4542,4230],{"class":1244},[1238,4544,3255],{"class":1244},[1238,4546,4547,4550,4552,4555,4558,4561],{"class":1240,"line":2042},[1238,4548,4549],{"class":3277},"    data",[1238,4551,1260],{"class":1244},[1238,4553,4554],{"class":1244}," new",[1238,4556,4557],{"class":1512}," Float32Array",[1238,4559,4560],{"class":1327},"(normalised)",[1238,4562,1272],{"class":1244},[1238,4564,4565,4568,4570,4572,4575,4577],{"class":1240,"line":2048},[1238,4566,4567],{"class":3277},"    type",[1238,4569,1260],{"class":1244},[1238,4571,1263],{"class":1244},[1238,4573,4574],{"class":1266},"float32",[1238,4576,1257],{"class":1244},[1238,4578,1272],{"class":1244},[1238,4580,4581,4584,4586,4588,4591,4593,4596],{"class":1240,"line":2054},[1238,4582,4583],{"class":3277},"    dims",[1238,4585,1260],{"class":1244},[1238,4587,1303],{"class":1327},[1238,4589,4590],{"class":1286},"1",[1238,4592,1309],{"class":1244},[1238,4594,4595],{"class":1286}," 33",[1238,4597,1408],{"class":1327},[1238,4599,4600],{"class":1240,"line":2060},[1238,4601,4602],{"class":1244},"};\n",[1238,4604,4605,4607,4609,4612,4614,4617,4619,4622],{"class":1240,"line":2066},[1238,4606,78],{"class":1327},[1238,4608,474],{"class":1244},[1238,4610,4611],{"class":1327},"payload ",[1238,4613,4230],{"class":1244},[1238,4615,4616],{"class":1327}," msg",[1238,4618,474],{"class":1244},[1238,4620,4621],{"class":1327},"input",[1238,4623,3285],{"class":1244},[1238,4625,4626,4628,4630,4633,4635,4637,4639,4641,4643,4645,4647,4649,4651],{"class":1240,"line":2072},[1238,4627,78],{"class":1327},[1238,4629,474],{"class":1244},[1238,4631,4632],{"class":1327},"threshold ",[1238,4634,4230],{"class":1244},[1238,4636,4233],{"class":1327},[1238,4638,474],{"class":1244},[1238,4640,4065],{"class":1512},[1238,4642,3245],{"class":1327},[1238,4644,4070],{"class":1244},[1238,4646,4197],{"class":1266},[1238,4648,4070],{"class":1244},[1238,4650,3252],{"class":1327},[1238,4652,3285],{"class":1244},[1238,4654,4655,4658,4660],{"class":1240,"line":2078},[1238,4656,4657],{"class":3672},"return",[1238,4659,4616],{"class":1327},[1238,4661,3285],{"class":1244},[15,4663,4664,4665,4667,4668,4671],{},"This function extracts 11 time-domain features per axis (33 total), loads the scaler on first run, and normalises the feature vector. The ",[76,4666,1682],{}," floor and ",[76,4669,4670],{},"±CLIP"," clamp mirror the values used during training and prevent near-constant features from producing extreme values, which are a common source of false positives with vibration sensors.",[118,4673,4674],{},[15,4675,4676,4679,4680,4682],{},[53,4677,4678],{},"If you changed the model file path",", update the two ",[76,4681,4106],{}," paths to match your chosen directory.",[15,4684,4685],{},[53,4686,4687],{},"3. Run the Model",[15,4689,3192,4690,4692],{},[53,4691,3109],{}," node and configure it:",[47,4694,4695,4704],{},[50,4696,4697,4700,4701],{},[53,4698,4699],{},"Model path:"," ",[76,4702,4703],{},"\u002Fopt\u002Fflowfuse-device\u002Fmodels\u002Fmotor_autoencoder.onnx",[50,4705,4706,4700,4709],{},[53,4707,4708],{},"Input:",[76,4710,4711],{},"msg.payload",[15,4713,4714,4715,4717],{},"The autoencoder compresses the 33-feature input through the bottleneck and reconstructs it on the output side. The reconstructed tensor is accessible in the next node via ",[76,4716,4711],{},". Since the output key name depends on how the ONNX graph was exported, the scoring node retrieves it dynamically rather than relying on a hardcoded name.",[15,4719,4720],{},[53,4721,4722],{},"4. Score the Reconstruction Error",[15,4724,4725,4726,4728],{},"Add a second ",[53,4727,3215],{}," node:",[1195,4730,4732],{"className":3231,"code":4731,"language":3233,"meta":187,"style":187},"if (!context.get('initialized')) {\n    flow.set('score_history', []);\n    context.set('initialized', true);\n}\n\n\u002F\u002F Generic output key lookup, works regardless of tensor name\nconst outputKey = Object.keys(msg.payload)[0];\nconst reconstructed = msg.payload[outputKey].cpuData;\n\nconst input = Array.from(msg.input.data);\nconst threshold = msg.threshold;\n\nconst mse = input.reduce((s, v, i) => s + (reconstructed[i] - v) ** 2, 0) \u002F input.length;\n\nlet history = flow.get('score_history') || [];\nhistory.push(mse);\nif (history.length > 10) history.shift();\nflow.set('score_history', history);\n\nconst smoothed = history.reduce((a, b) => a + b, 0) \u002F history.length;\n\nmsg.anomaly_score = smoothed;\nmsg.is_anomaly    = smoothed > threshold;\nmsg.severity      = smoothed > threshold * 2 ? 'CRITICAL' : smoothed > threshold ? 'WARNING' : 'NORMAL';\nmsg.payload       = { anomaly_score: smoothed, threshold, is_anomaly: msg.is_anomaly, severity: msg.severity };\nreturn msg;\n",[76,4733,4734,4762,4786,4813,4817,4821,4826,4857,4880,4884,4914,4931,4935,5004,5008,5041,5056,5087,5110,5114,5167,5171,5187,5207,5270,5325],{"__ignoreMap":187},[1238,4735,4736,4738,4740,4742,4745,4747,4749,4751,4753,4756,4758,4760],{"class":1240,"line":1241},[1238,4737,4052],{"class":3672},[1238,4739,3404],{"class":1327},[1238,4741,4057],{"class":1244},[1238,4743,4744],{"class":1327},"context",[1238,4746,474],{"class":1244},[1238,4748,4065],{"class":1512},[1238,4750,3245],{"class":1327},[1238,4752,4070],{"class":1244},[1238,4754,4755],{"class":1266},"initialized",[1238,4757,4070],{"class":1244},[1238,4759,4078],{"class":1327},[1238,4761,1245],{"class":1244},[1238,4763,4764,4766,4768,4770,4772,4774,4777,4779,4781,4784],{"class":1240,"line":191},[1238,4765,4161],{"class":1327},[1238,4767,474],{"class":1244},[1238,4769,4166],{"class":1512},[1238,4771,3245],{"class":3277},[1238,4773,4070],{"class":1244},[1238,4775,4776],{"class":1266},"score_history",[1238,4778,4070],{"class":1244},[1238,4780,1309],{"class":1244},[1238,4782,4783],{"class":3277}," [])",[1238,4785,3285],{"class":1244},[1238,4787,4788,4791,4793,4795,4797,4799,4801,4803,4805,4809,4811],{"class":1240,"line":196},[1238,4789,4790],{"class":1327},"    context",[1238,4792,474],{"class":1244},[1238,4794,4166],{"class":1512},[1238,4796,3245],{"class":3277},[1238,4798,4070],{"class":1244},[1238,4800,4755],{"class":1266},[1238,4802,4070],{"class":1244},[1238,4804,1309],{"class":1244},[1238,4806,4808],{"class":4807},"sfNiH"," true",[1238,4810,3252],{"class":3277},[1238,4812,3285],{"class":1244},[1238,4814,4815],{"class":1240,"line":188},[1238,4816,1414],{"class":1244},[1238,4818,4819],{"class":1240,"line":1334},[1238,4820,1789],{"emptyLinePlaceholder":216},[1238,4822,4823],{"class":1240,"line":1372},[1238,4824,4825],{"class":4041},"\u002F\u002F Generic output key lookup, works regardless of tensor name\n",[1238,4827,4828,4830,4833,4835,4838,4840,4843,4845,4847,4850,4853,4855],{"class":1240,"line":1411},[1238,4829,4224],{"class":1253},[1238,4831,4832],{"class":1327}," outputKey ",[1238,4834,4230],{"class":1244},[1238,4836,4837],{"class":1327}," Object",[1238,4839,474],{"class":1244},[1238,4841,4842],{"class":1512},"keys",[1238,4844,4314],{"class":1327},[1238,4846,474],{"class":1244},[1238,4848,4849],{"class":1327},"payload)[",[1238,4851,4852],{"class":1286},"0",[1238,4854,4367],{"class":1327},[1238,4856,3285],{"class":1244},[1238,4858,4859,4861,4864,4866,4868,4870,4873,4875,4878],{"class":1240,"line":1807},[1238,4860,4224],{"class":1253},[1238,4862,4863],{"class":1327}," reconstructed ",[1238,4865,4230],{"class":1244},[1238,4867,4616],{"class":1327},[1238,4869,474],{"class":1244},[1238,4871,4872],{"class":1327},"payload[outputKey]",[1238,4874,474],{"class":1244},[1238,4876,4877],{"class":1327},"cpuData",[1238,4879,3285],{"class":1244},[1238,4881,4882],{"class":1240,"line":1813},[1238,4883,1789],{"emptyLinePlaceholder":216},[1238,4885,4886,4888,4891,4893,4896,4898,4901,4903,4905,4907,4909,4912],{"class":1240,"line":1819},[1238,4887,4224],{"class":1253},[1238,4889,4890],{"class":1327}," input ",[1238,4892,4230],{"class":1244},[1238,4894,4895],{"class":1327}," Array",[1238,4897,474],{"class":1244},[1238,4899,4900],{"class":1512},"from",[1238,4902,4314],{"class":1327},[1238,4904,474],{"class":1244},[1238,4906,4621],{"class":1327},[1238,4908,474],{"class":1244},[1238,4910,4911],{"class":1327},"data)",[1238,4913,3285],{"class":1244},[1238,4915,4916,4918,4921,4923,4925,4927,4929],{"class":1240,"line":1825},[1238,4917,4224],{"class":1253},[1238,4919,4920],{"class":1327}," threshold ",[1238,4922,4230],{"class":1244},[1238,4924,4616],{"class":1327},[1238,4926,474],{"class":1244},[1238,4928,4197],{"class":1327},[1238,4930,3285],{"class":1244},[1238,4932,4933],{"class":1240,"line":1831},[1238,4934,1789],{"emptyLinePlaceholder":216},[1238,4936,4937,4939,4942,4944,4947,4949,4951,4953,4955,4958,4960,4963,4965,4967,4969,4971,4974,4976,4979,4981,4984,4986,4988,4990,4992,4994,4996,4998,5000,5002],{"class":1240,"line":1837},[1238,4938,4224],{"class":1253},[1238,4940,4941],{"class":1327}," mse ",[1238,4943,4230],{"class":1244},[1238,4945,4946],{"class":1327}," input",[1238,4948,474],{"class":1244},[1238,4950,3319],{"class":1512},[1238,4952,3245],{"class":1327},[1238,4954,3245],{"class":1244},[1238,4956,4957],{"class":3248},"s",[1238,4959,1309],{"class":1244},[1238,4961,4962],{"class":3248}," v",[1238,4964,1309],{"class":1244},[1238,4966,4401],{"class":3248},[1238,4968,3252],{"class":1244},[1238,4970,3335],{"class":1253},[1238,4972,4973],{"class":1327}," s ",[1238,4975,3435],{"class":1244},[1238,4977,4978],{"class":1327}," (reconstructed[i] ",[1238,4980,3704],{"class":1244},[1238,4982,4983],{"class":1327}," v) ",[1238,4985,3417],{"class":1244},[1238,4987,3420],{"class":1286},[1238,4989,1309],{"class":1244},[1238,4991,3348],{"class":1286},[1238,4993,3351],{"class":1327},[1238,4995,1452],{"class":1244},[1238,4997,4946],{"class":1327},[1238,4999,474],{"class":1244},[1238,5001,3301],{"class":1327},[1238,5003,3285],{"class":1244},[1238,5005,5006],{"class":1240,"line":1843},[1238,5007,1789],{"emptyLinePlaceholder":216},[1238,5009,5010,5013,5016,5018,5020,5022,5024,5026,5028,5030,5032,5034,5036,5039],{"class":1240,"line":1849},[1238,5011,5012],{"class":1253},"let",[1238,5014,5015],{"class":1327}," history ",[1238,5017,4230],{"class":1244},[1238,5019,4233],{"class":1327},[1238,5021,474],{"class":1244},[1238,5023,4065],{"class":1512},[1238,5025,3245],{"class":1327},[1238,5027,4070],{"class":1244},[1238,5029,4776],{"class":1266},[1238,5031,4070],{"class":1244},[1238,5033,3351],{"class":1327},[1238,5035,4269],{"class":1244},[1238,5037,5038],{"class":1327}," []",[1238,5040,3285],{"class":1244},[1238,5042,5043,5046,5048,5051,5054],{"class":1240,"line":1855},[1238,5044,5045],{"class":1327},"history",[1238,5047,474],{"class":1244},[1238,5049,5050],{"class":1512},"push",[1238,5052,5053],{"class":1327},"(mse)",[1238,5055,3285],{"class":1244},[1238,5057,5058,5060,5063,5065,5068,5071,5074,5077,5079,5082,5085],{"class":1240,"line":1860},[1238,5059,4052],{"class":3672},[1238,5061,5062],{"class":1327}," (history",[1238,5064,474],{"class":1244},[1238,5066,5067],{"class":1327},"length ",[1238,5069,5070],{"class":1244},">",[1238,5072,5073],{"class":1286}," 10",[1238,5075,5076],{"class":1327},") history",[1238,5078,474],{"class":1244},[1238,5080,5081],{"class":1512},"shift",[1238,5083,5084],{"class":1327},"()",[1238,5086,3285],{"class":1244},[1238,5088,5089,5091,5093,5095,5097,5099,5101,5103,5105,5108],{"class":1240,"line":1866},[1238,5090,4060],{"class":1327},[1238,5092,474],{"class":1244},[1238,5094,4166],{"class":1512},[1238,5096,3245],{"class":1327},[1238,5098,4070],{"class":1244},[1238,5100,4776],{"class":1266},[1238,5102,4070],{"class":1244},[1238,5104,1309],{"class":1244},[1238,5106,5107],{"class":1327}," history)",[1238,5109,3285],{"class":1244},[1238,5111,5112],{"class":1240,"line":1872},[1238,5113,1789],{"emptyLinePlaceholder":216},[1238,5115,5116,5118,5121,5123,5126,5128,5130,5132,5134,5136,5138,5140,5142,5144,5147,5149,5151,5153,5155,5157,5159,5161,5163,5165],{"class":1240,"line":1878},[1238,5117,4224],{"class":1253},[1238,5119,5120],{"class":1327}," smoothed ",[1238,5122,4230],{"class":1244},[1238,5124,5125],{"class":1327}," history",[1238,5127,474],{"class":1244},[1238,5129,3319],{"class":1512},[1238,5131,3245],{"class":1327},[1238,5133,3245],{"class":1244},[1238,5135,22],{"class":3248},[1238,5137,1309],{"class":1244},[1238,5139,3330],{"class":3248},[1238,5141,3252],{"class":1244},[1238,5143,3335],{"class":1253},[1238,5145,5146],{"class":1327}," a ",[1238,5148,3435],{"class":1244},[1238,5150,3330],{"class":1327},[1238,5152,1309],{"class":1244},[1238,5154,3348],{"class":1286},[1238,5156,3351],{"class":1327},[1238,5158,1452],{"class":1244},[1238,5160,5125],{"class":1327},[1238,5162,474],{"class":1244},[1238,5164,3301],{"class":1327},[1238,5166,3285],{"class":1244},[1238,5168,5169],{"class":1240,"line":1884},[1238,5170,1789],{"emptyLinePlaceholder":216},[1238,5172,5173,5175,5177,5180,5182,5185],{"class":1240,"line":1890},[1238,5174,78],{"class":1327},[1238,5176,474],{"class":1244},[1238,5178,5179],{"class":1327},"anomaly_score ",[1238,5181,4230],{"class":1244},[1238,5183,5184],{"class":1327}," smoothed",[1238,5186,3285],{"class":1244},[1238,5188,5189,5191,5193,5196,5198,5200,5202,5205],{"class":1240,"line":1896},[1238,5190,78],{"class":1327},[1238,5192,474],{"class":1244},[1238,5194,5195],{"class":1327},"is_anomaly    ",[1238,5197,4230],{"class":1244},[1238,5199,5120],{"class":1327},[1238,5201,5070],{"class":1244},[1238,5203,5204],{"class":1327}," threshold",[1238,5206,3285],{"class":1244},[1238,5208,5209,5211,5213,5216,5218,5220,5222,5224,5227,5229,5232,5235,5238,5240,5243,5245,5247,5249,5252,5254,5257,5259,5261,5263,5266,5268],{"class":1240,"line":1902},[1238,5210,78],{"class":1327},[1238,5212,474],{"class":1244},[1238,5214,5215],{"class":1327},"severity      ",[1238,5217,4230],{"class":1244},[1238,5219,5120],{"class":1327},[1238,5221,5070],{"class":1244},[1238,5223,4920],{"class":1327},[1238,5225,5226],{"class":1244},"*",[1238,5228,3420],{"class":1286},[1238,5230,5231],{"class":1244}," ?",[1238,5233,5234],{"class":1244}," '",[1238,5236,5237],{"class":1266},"CRITICAL",[1238,5239,4070],{"class":1244},[1238,5241,5242],{"class":1244}," :",[1238,5244,5120],{"class":1327},[1238,5246,5070],{"class":1244},[1238,5248,4920],{"class":1327},[1238,5250,5251],{"class":1244},"?",[1238,5253,5234],{"class":1244},[1238,5255,5256],{"class":1266},"WARNING",[1238,5258,4070],{"class":1244},[1238,5260,5242],{"class":1244},[1238,5262,5234],{"class":1244},[1238,5264,5265],{"class":1266},"NORMAL",[1238,5267,4070],{"class":1244},[1238,5269,3285],{"class":1244},[1238,5271,5272,5274,5276,5279,5281,5284,5287,5289,5291,5293,5295,5297,5300,5302,5304,5306,5309,5311,5314,5316,5318,5320,5323],{"class":1240,"line":1907},[1238,5273,78],{"class":1327},[1238,5275,474],{"class":1244},[1238,5277,5278],{"class":1327},"payload       ",[1238,5280,4230],{"class":1244},[1238,5282,5283],{"class":1244}," {",[1238,5285,5286],{"class":3277}," anomaly_score",[1238,5288,1260],{"class":1244},[1238,5290,5184],{"class":1327},[1238,5292,1309],{"class":1244},[1238,5294,5204],{"class":1327},[1238,5296,1309],{"class":1244},[1238,5298,5299],{"class":3277}," is_anomaly",[1238,5301,1260],{"class":1244},[1238,5303,4616],{"class":1327},[1238,5305,474],{"class":1244},[1238,5307,5308],{"class":1327},"is_anomaly",[1238,5310,1309],{"class":1244},[1238,5312,5313],{"class":3277}," severity",[1238,5315,1260],{"class":1244},[1238,5317,4616],{"class":1327},[1238,5319,474],{"class":1244},[1238,5321,5322],{"class":1327},"severity ",[1238,5324,4602],{"class":1244},[1238,5326,5327,5329,5331],{"class":1240,"line":1913},[1238,5328,4657],{"class":3672},[1238,5330,4616],{"class":1327},[1238,5332,3285],{"class":1244},[15,5334,5335,5336,1106,5338,5340,5341,474],{},"This function computes mean squared error between the model's reconstruction and the normalised input, applies a 10-window rolling average to reduce sensitivity to transient spikes, then classifies the result as ",[76,5337,5265],{},[76,5339,5256],{},", or ",[76,5342,5237],{},[118,5344,5345],{},[15,5346,5347,5350],{},[53,5348,5349],{},"Recovery time:"," After an anomaly clears, the score returns to normal once the rolling window fills with healthy readings , typically 10 × your publish interval. At 500 ms publishing, that's roughly 5 seconds. Reduce the history window size for faster recovery; increase it to suppress false alarms.",[15,5352,5353],{},"Once deployed, the flow should look like this:",[15,5355,5356],{},[30,5357],{"alt":5358,"src":5359,"title":5358},"Completed Node-RED inference flow showing MQTT input, feature extraction function node, ONNX node, and anomaly scoring function node","\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fflow.png",[15,5361,5362],{},[53,5363,5364],{},"5. Act on the Result",[15,5366,5367,5368,5373,5374,1106,5378,5340,5382,5386],{},"Connect the scoring output to whatever suits your operation. For testing, a debug node shows results in real time. For production, an mqtt-out node can publish anomaly alerts downstream, the ",[22,5369,5372],{"href":5370,"rel":5371},"https:\u002F\u002Fdashboard.flowfuse.com",[445],"FlowFuse Dashboard"," package can visualise the anomaly score over time with a clear motor state indicator, and alerting nodes can notify your team directly, via ",[22,5375,5377],{"href":5376},"\u002Fnode-red\u002Fnotification\u002Femail\u002F","Telegram",[22,5379,5381],{"href":5380},"\u002Fnode-red\u002Fnotification\u002Ftelegram\u002F","email",[22,5383,5385],{"href":5384},"\u002Fblueprints\u002Fother\u002Fmobile-alerting\u002F","SIGNL4"," for structured mobile alerts with on-call scheduling and acknowledgement tracking.",[143,5388,5390],{"id":5389},"what-the-output-looks-like","What the Output Looks Like",[15,5392,5393],{},"Each message produces a structured result:",[1195,5395,5397],{"className":1232,"code":5396,"language":1234,"meta":187,"style":187},"{\n  \"anomaly_score\": 0.842,\n  \"threshold\": 0.703,\n  \"is_anomaly\": true,\n  \"severity\": \"WARNING\"\n}\n",[76,5398,5399,5403,5419,5434,5447,5465],{"__ignoreMap":187},[1238,5400,5401],{"class":1240,"line":1241},[1238,5402,1245],{"class":1244},[1238,5404,5405,5407,5410,5412,5414,5417],{"class":1240,"line":191},[1238,5406,1250],{"class":1244},[1238,5408,5409],{"class":1253},"anomaly_score",[1238,5411,1257],{"class":1244},[1238,5413,1260],{"class":1244},[1238,5415,5416],{"class":1286}," 0.842",[1238,5418,1272],{"class":1244},[1238,5420,5421,5423,5425,5427,5429,5432],{"class":1240,"line":196},[1238,5422,1250],{"class":1244},[1238,5424,4197],{"class":1253},[1238,5426,1257],{"class":1244},[1238,5428,1260],{"class":1244},[1238,5430,5431],{"class":1286}," 0.703",[1238,5433,1272],{"class":1244},[1238,5435,5436,5438,5440,5442,5444],{"class":1240,"line":188},[1238,5437,1250],{"class":1244},[1238,5439,5308],{"class":1253},[1238,5441,1257],{"class":1244},[1238,5443,1260],{"class":1244},[1238,5445,5446],{"class":1244}," true,\n",[1238,5448,5449,5451,5454,5456,5458,5460,5462],{"class":1240,"line":1334},[1238,5450,1250],{"class":1244},[1238,5452,5453],{"class":1253},"severity",[1238,5455,1257],{"class":1244},[1238,5457,1260],{"class":1244},[1238,5459,1263],{"class":1244},[1238,5461,5256],{"class":1266},[1238,5463,5464],{"class":1244},"\"\n",[1238,5466,5467],{"class":1240,"line":1372},[1238,5468,1414],{"class":1244},[15,5470,5471,5472,5474,5475,5478,5479,5482,5483,5485],{},"When ",[76,5473,5308],{}," is ",[76,5476,5477],{},"false",", the motor is behaving within the expected range. When it flips to ",[76,5480,5481],{},"true",", the vibration pattern has shifted beyond the acceptable boundary, giving you time to act before the problem becomes a failure. A severity of ",[76,5484,5237],{}," means the score has crossed twice the threshold, signalling a more significant deviation that warrants immediate attention.",[39,5487,5489],{"id":5488},"what-this-system-wont-tell-you","What This System Won't Tell You",[15,5491,5492],{},"This approach works well, but it's worth being clear about where it stops.",[15,5494,5495],{},"The autoencoder learns a statistical boundary around the vibration patterns it was trained on. It doesn't understand physics, it doesn't know the difference between a worn bearing and a loose mounting bolt, and it has no concept of severity beyond the reconstruction error score. When it flags an anomaly, it's telling you that something has changed, not what changed or why. Diagnosing the root cause still requires a technician with domain knowledge.",[15,5497,5498],{},"Training data quality matters more than model architecture. A model trained on data collected while the motor was lightly loaded, recently serviced, or running in cool ambient conditions will treat those as \"normal.\" If real operating conditions differ, the threshold may be poorly calibrated from day one, generating either chronic false positives or, worse, missing genuine faults. There's no substitute for collecting training data under representative, sustained, real-world load.",[15,5500,5501,5502,5504],{},"False positives are inevitable in early deployment. External vibration from nearby equipment, transient load spikes, or sensor cable movement can all push the score above threshold momentarily. The rolling average window helps, but it doesn't eliminate them. Treat the first few weeks as a calibration period: log alerts, investigate them, and adjust ",[76,5503,1722],{}," or the window size based on what you learn. The system improves with attention.",[15,5506,5507],{},"Finally, anomaly detection is an early warning layer, not a maintenance strategy on its own. It tells you to look sooner, not what to do when you get there. Pair it with regular physical inspection, lubrication schedules, and, where possible, a domain expert who can interpret the alerts in context. Used that way, it earns its place. Used as a replacement for those things, it will eventually let you down.",[5509,5510,5511],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":187,"searchDepth":188,"depth":188,"links":5513},[5514,5517,5518,5522,5528,5534],{"id":1176,"depth":191,"text":1177,"children":5515},[5516],{"id":1183,"depth":196,"text":1184},{"id":1211,"depth":191,"text":1212},{"id":1218,"depth":191,"text":1219,"children":5519},[5520,5521],{"id":1225,"depth":196,"text":1226},{"id":1437,"depth":196,"text":1438},{"id":1468,"depth":191,"text":1469,"children":5523},[5524,5525,5526,5527],{"id":1475,"depth":196,"text":1476},{"id":1568,"depth":196,"text":1569},{"id":1751,"depth":196,"text":1752},{"id":3052,"depth":196,"text":3053},{"id":3059,"depth":191,"text":3060,"children":5529},[5530,5531,5532,5533],{"id":3066,"depth":196,"text":3067},{"id":3119,"depth":196,"text":3120},{"id":3180,"depth":196,"text":3181},{"id":5389,"depth":196,"text":5390},{"id":5488,"depth":191,"text":5489},{"type":603,"title":5536,"description":5537},"See How FlowFuse Catches Faults Before They Fail","Book a demo and see how FlowFuse brings sensor ingestion, AI inference, and real-time alerting together on the factory floor, no separate ML infrastructure required","2026-02-20","Learn how to monitor industrial motors continuously, train a custom autoencoder on healthy vibration data, and deploy real-time anomaly detection in Node-RED.","\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fmotor-anomaly-detection-ai.png","2026-06-19",{"excerpt":5543},{"type":12,"value":5544},[5545],[15,5546,1162],{},"\u002Fblog\u002F2026\u002F02\u002Fmotor-anomaly-detector-ai",{"title":1156,"description":5539},{"loc":5547},"blog\u002F2026\u002F02\u002Fmotor-anomaly-detector-ai","Detect motor faults early using AI-driven vibration analysis and anomaly detection.",[223,224],"This guide walks through building an AI-based motor vibration anomaly detector using an autoencoder trained on healthy sensor data, exported to ONNX, and deployed directly in Node-RED via FlowFuse's AI nodes no separate ML infrastructure required. The system extracts 33 time-domain features per vibration window, scores each reading against a trained threshold, and classifies results as NORMAL, WARNING, or CRITICAL in real time.","dKh4oY175vEeszYo4Ax7ZKFYr4WnsXff6kt-feFDHoY",{"id":5556,"title":5557,"authors":5558,"body":5559,"cta":5831,"date":5834,"description":5835,"extension":207,"image":5836,"lastUpdated":5837,"meta":5838,"navigation":216,"path":5844,"seo":5845,"sitemap":5846,"stem":5847,"subtitle":5848,"tags":5849,"tldr":5850,"video":3,"__hash__":5851},"blog\u002Fblog\u002F2026\u002F02\u002Fshop-floor-to-ai-signals-context-decisions.md","Shop Floor AI: Dead on Arrival Without This",[10],{"type":12,"value":5560,"toc":5821},[5561,5564,5567,5570,5573,5576,5579,5582,5585,5588,5592,5595,5598,5601,5605,5608,5611,5614,5617,5621,5624,5627,5630,5633,5636,5640,5643,5646,5649,5652,5655,5658,5662,5665,5668,5671,5674,5677,5680,5684,5687,5690,5693,5696,5703,5706,5713,5716,5719,5730,5733,5739,5742,5745,5748,5756,5773,5781,5784,5788,5791,5794,5797,5800,5803,5806,5809,5812],[15,5562,5563],{},"Your industrial AI initiative is dying. Maybe it's already dead.",[15,5565,5566],{},"Not because the models are wrong. Not because the data scientists failed. Not because you didn't spend enough on sensors or compute power.",[15,5568,5569],{},"It's dying because you're building on a foundation that was never designed to support it.",[15,5571,5572],{},"You instrumented everything: motors, conveyors, bearings, valves, streaming thousands of data points per second. Historians filled to capacity. Dashboards displayed every metric. AI models trained on millions of records. Yet despite all this technology, you still can't see what's happening until something breaks.",[15,5574,5575],{},"The problem isn't your AI. It's the architecture underneath it.",[15,5577,5578],{},"This article reveals why most industrial AI projects fail before they start: why raw signals without context are just noise, why your three disconnected data layers doom AI from day one, and why a Unified Namespace is the only architecture that makes industrial AI actually work on the shop floor.",[15,5580,5581],{},"Twenty years ago, a skilled operator could diagnose a failing machine by sound, smell, or vibration. Today's machines still communicate just as clearly. They've simply switched languages. They produce numbers that nobody understands. A temperature spike, a current drift, a vibration anomaly: each is meaningless without knowing which product is running, under what conditions, with which maintenance history, and how this system typically behaves.",[15,5583,5584],{},"The problem isn't AI capability. It's poor architecture. Signals without context are difficult to interpret. Context without connection never reaches the people who need it. And decisions made without information are guesses at best.",[15,5586,5587],{},"For AI to actually work on the factory floor, we need three things working in concert: signals that feed context, context that creates understanding, and AI that empowers humans to ask the right questions at the right time.",[39,5589,5591],{"id":5590},"the-three-layer-problem","The Three-Layer Problem",[15,5593,5594],{},"Most manufacturers diagnose themselves with an AI problem. Their models don't predict failures. Their anomaly detection drowns in false positives. Their optimization recommendations get politely ignored.",[15,5596,5597],{},"They're diagnosing the wrong disease. Most factory floors aren't ready for AI.",[15,5599,5600],{},"This isn't an AI problem. It's an architecture problem that AI just makes impossible to ignore. Your data exists in three disconnected layers, and until you bridge them, no amount of machine learning can help.",[143,5602,5604],{"id":5603},"layer-one-the-signal-layer","Layer One: The Signal Layer",[15,5606,5607],{},"Raw data accumulates here. PLCs, SCADA, historians, MES systems, all generating measurements at rates human cognition was never designed to process. Temperature, pressure, flow, current draw, RPM, torque, position. Millisecond timestamps. Perfect fidelity. Absolutely zero meaning.",[15,5609,5610],{},"The signal layer has no concept of importance. When a conveyor motor pulls 2.3 amps, that's just a number in a database. The system doesn't know if this represents peak efficiency or the warning sign of a dying gearbox.",[15,5612,5613],{},"But nobody knows which question to ask until something fails. Then you're analyzing historical data files, reconstructing what happened. It's post-incident analysis when what you needed was real-time diagnosis.",[15,5615,5616],{},"The signal layer does exactly one thing well: it remembers everything. What it can't do is understand anything.",[143,5618,5620],{"id":5619},"layer-two-the-context-layer","Layer Two: The Context Layer",[15,5622,5623],{},"Context is everything the signal doesn't tell you. Which product is currently running. The ambient conditions. The maintenance history. The supplier change that wasn't documented. The operator who runs things hot because it's faster. The firmware update that altered control loop timing.",[15,5625,5626],{},"This layer exists in fragments, scattered across ERP systems, maintenance logs, Excel files, shift handover notes, and inside the heads of people who might retire next year.",[15,5628,5629],{},"Without this layer, signals are just sequential numbers. With it, they become useful information. They tell you not just what is happening, but why it matters, what it resembles, and what typically comes next.",[15,5631,5632],{},"The fundamental problem: we never built systems to unite these layers. Different databases, different teams, different vendors, different security models. Integration became a six-month IT project instead of a core design principle.",[15,5634,5635],{},"Your data has context, but it's locked away where neither your people nor your AI can see it.",[143,5637,5639],{"id":5638},"layer-three-the-human-decision-layer","Layer Three: The Human Decision Layer",[15,5641,5642],{},"This is where humans operate, increasingly overwhelmed by the gap between what they can see and what they need to know.",[15,5644,5645],{},"An alarm sounds. An operator has 30 seconds to decide: Is this real or noise? Critical or routine? Stop the line or log and monitor? The context they need is fragmented across three systems they can't access and two colleagues on different shifts.",[15,5647,5648],{},"So they decide based on experience and instinct. Sometimes they're right. Sometimes they're not. Either way, the decision logic gets lost - there's no system capturing why they chose what they did.",[15,5650,5651],{},"Engineers face the inverse problem: too much time and too much data. By the time they've extracted historian data, correlated it with production schedules, and cross-referenced maintenance records, the problem has either resolved itself or gotten worse.",[15,5653,5654],{},"This is where AI should enter, not as a decision-maker, but as an intelligent assistant. The human decision layer needs AI that can answer questions in real-time: \"Is this vibration pattern normal for this product recipe?\" \"When did we last see this current signature?\" \"What were the conditions the last three times this alarm triggered?\"",[15,5656,5657],{},"The decision remains human. The insight becomes instant.",[39,5659,5661],{"id":5660},"why-this-architecture-breaks-ai","Why This Architecture Breaks AI",[15,5663,5664],{},"You can't fix a three-layer problem with a one-layer solution.",[15,5666,5667],{},"Companies repeatedly make the same mistake: they drop AI models directly into the signal layer (pure time-series analysis on raw sensor data) then wonder why predictions are worthless. The model identifies a pattern, but it's blind to the fact that context just changed. It flags anomalies that are actually normal for this product recipe. It misses failures because the signal appeared fine while the context indicated problems.",[15,5669,5670],{},"But here's what's crucial to understand: AI is ready for the factory floor right now. Not ready to take autonomous action, but ready to be the most knowledgeable assistant your operators and engineers have ever had.",[15,5672,5673],{},"Think about what you actually need. When an operator sees unusual behavior, they need answers immediately: \"Is this normal?\" \"What happened last time?\" \"Should I be concerned?\" When an engineer investigates a problem, they need to explore data at depth: \"Show me all the times we saw this pattern.\" \"What were the ambient conditions?\" \"How does this compare across shifts?\"",[15,5675,5676],{},"AI can answer these questions instantly if it has access to the right architecture.",[15,5678,5679],{},"Industrial AI fails when you ignore the architecture. You need the signal layer feeding a context layer that's actually integrated, queryable, and current. You need decision support that operates at the speed questions get asked, not at the speed IT can generate a report.",[39,5681,5683],{"id":5682},"the-architecture-solution","The Architecture Solution",[15,5685,5686],{},"The challenge isn't the layers themselves, but the gaps between them.",[15,5688,5689],{},"So what would an architecture look like that actually closes these gaps? What would it take to have signals arrive already carrying context? To have that context accessible the moment a question gets asked? To give AI and humans the same unified view of what's happening right now?",[15,5691,5692],{},"The requirements are clear: you need operational data organized the way factories actually run - by site, area, line, and asset. You need context added at the moment data enters the system, not reconstructed hours later. You need a single source of truth that every system can access in real time.",[15,5694,5695],{},"This isn't a future vision. This architecture exists, and it's been battle-tested in manufacturing operations worldwide.",[15,5697,5698,5699,474],{},"It's called the ",[22,5700,5702],{"href":5701},"\u002Fblog\u002F2023\u002F12\u002Fintroduction-to-unified-namespace\u002F","Unified Namespace (UNS)",[15,5704,5705],{},"A Unified Namespace is a shared, real-time, event-driven structure where operational data flows with its context intact. Instead of systems integrating point-to-point, every system publishes to and consumes from the same namespace. Signals arrive already carrying context.",[15,5707,5708,5709,5712],{},"In a UNS, a motor current is no longer just a number stored in a historian. It's published as ",[35,5710,5711],{},"Line 3 \u002F Conveyor 2B \u002F Motor Current",", alongside the active recipe, operating mode, ambient conditions, and relevant maintenance history. Every system sees the same structured truth, continuously updated.",[15,5714,5715],{},"This shift in architecture is what makes AI viable on the factory floor.",[15,5717,5718],{},"Building a Unified Namespace requires three things:",[3084,5720,5721,5724,5727],{},[50,5722,5723],{},"Connecting incompatible industrial systems",[50,5725,5726],{},"Enriching raw signals with operational context as data flows",[50,5728,5729],{},"Publishing that context once, over MQTT, so AI and humans can consume it in real time",[15,5731,5732],{},"This is where flow-based integration becomes essential.",[15,5734,5735,5736,5738],{},"Tools like ",[22,5737,438],{"href":1043}," make UNS architectures practical. Instead of writing custom integration code, engineers visually wire systems together. PLCs publishing over Modbus, MES systems exposing REST APIs, and proprietary SCADA protocols can all be connected, normalized, and enriched as data moves through the flows.",[15,5740,5741],{},"FlowFuse builds on Node-RED to make this architecture production-ready. It adds centralized deployment, version control, access control, and remote management: the capabilities required to operate a Unified Namespace reliably across lines, plants, and teams.",[15,5743,5744],{},"Crucially, in a Unified Namespace, context is added at the moment data enters the system, not reconstructed later. A motor current isn't simply forwarded. It's enriched with equipment hierarchy, product recipe, operating mode, environmental conditions, and timestamps aligned with production events.",[15,5746,5747],{},"That enriched information is then published into a shared MQTT-based Namespace. One location. One structure. One source of truth. Dashboards, analytics, and AI systems all subscribe to the same contextualized view of reality.",[15,5749,5750,5751,5755],{},"Through ",[22,5752,5754],{"href":5753},"\u002Fnode-red\u002Fflowfuse\u002Fmcp\u002F","FlowFuse MCP nodes",", AI systems connect directly to the namespace, querying live operational context instead of pulling raw time-series data from isolated historians and attempting to reconstruct meaning after the fact.",[15,5757,5758,5762,5763,1106,5766,1106,5769,5772],{},[22,5759,5761],{"href":5760},"\u002Fai\u002F","FlowFuse AI Expert"," operates on the same MCP-backed context layer. Operators and engineers can ask questions in natural language (",[35,5764,5765],{},"\"Is Line 3 behaving normally?\"",[35,5767,5768],{},"\"Have we seen this vibration pattern before?\"",[35,5770,5771],{},"\"What changed before the last failure?\"",") and receive answers grounded in the live Unified Namespace.",[15,5774,5775,5776,474],{},"To learn how to build your own Unified Namespace with FlowFuse, ",[22,5777,5780],{"href":5778,"rel":5779},"https:\u002F\u002Fflowfuse.com\u002Fblog\u002F2024\u002F11\u002Fbuilding-uns-with-flowfuse\u002F",[445],"see our comprehensive guide",[15,5782,5783],{},"The result is immediate insight without additional tooling, custom integrations, or fragile data pipelines. The architecture already exists. The context is already there. The questions can finally be asked at the speed decisions are made.",[39,5785,5787],{"id":5786},"final-thoughts","Final Thoughts",[15,5789,5790],{},"Your industrial AI isn't failing because the models are bad. It's failing because the architecture was never designed to support it.",[15,5792,5793],{},"Most manufacturers make the same mistake: they bolt AI onto existing infrastructure - historians full of raw signals, context scattered across disconnected systems, decisions made with incomplete information. Then they wonder why predictions are worthless and anomaly detection drowns in false positives.",[15,5795,5796],{},"You can't solve a three-layer problem with a one-layer solution.",[15,5798,5799],{},"The Unified Namespace fixes this by doing what should have been done from the start: uniting signals with context in real time. A motor current stops being \"2.3 amps\" in a database and becomes operational intelligence - which line, which equipment, which recipe, what maintenance history, what patterns preceded past failures.",[15,5801,5802],{},"This is the foundation AI needs. Not more data. Not better models. Context that transforms signals into understanding.",[15,5804,5805],{},"With this architecture in place, AI shifts from a failed prediction engine to what it should be: a tool that multiplies operational expertise. It doesn't replace human judgment. It enables faster, better-informed decisions backed by complete operational context.",[15,5807,5808],{},"Manufacturers who build this architecture first get operations that learn from every incident, engineering teams that diagnose root causes in minutes instead of days, and confidence in decisions because they're based on understanding rather than guesswork.",[15,5810,5811],{},"The path forward isn't better AI models. It's better architecture. Build the Unified Namespace first. The AI will finally work.",[15,5813,5814],{},[35,5815,5816,5820],{},[22,5817,5819],{"href":5818},"\u002Fcontact-us\u002F","Start with FlowFuse today",". Build the architecture your industrial AI needs to succeed.",{"title":187,"searchDepth":188,"depth":188,"links":5822},[5823,5828,5829,5830],{"id":5590,"depth":191,"text":5591,"children":5824},[5825,5826,5827],{"id":5603,"depth":196,"text":5604},{"id":5619,"depth":196,"text":5620},{"id":5638,"depth":196,"text":5639},{"id":5660,"depth":191,"text":5661},{"id":5682,"depth":191,"text":5683},{"id":5786,"depth":191,"text":5787},{"type":974,"title":5832,"description":5833},"Build the Architecture Your AI Actually Needs","FlowFuse helps you connect industrial systems, enrich signals with context, and publish to a Unified Namespace so your AI finally has something useful to work with.","2026-02-06","Industrial AI doesn't fail because of bad models - it fails because of bad architecture. Discover why signals need context and how a Unified Namespace makes AI work on the shop floor.","\u002Fblog\u002F2026\u002F02\u002Fimages\u002Fshopfloor-to-ai.png","2026-02-11",{"keywords":5839,"excerpt":5840},"industrial AI, Unified Namespace, shop floor, signals, context, human decision layer, FlowFuse, Node-RED, operational data, real-time insights, factory automation, manufacturing AI",{"type":12,"value":5841},[5842],[15,5843,5563],{},"\u002Fblog\u002F2026\u002F02\u002Fshop-floor-to-ai-signals-context-decisions",{"title":5557,"description":5835},{"loc":5844},"blog\u002F2026\u002F02\u002Fshop-floor-to-ai-signals-context-decisions","Why your industrial AI fails before it even starts - and the missing architecture that fixes it",[223,224],"Industrial AI fails not because of bad models but because raw signals lack the operational context product recipe, maintenance history, ambient conditions that makes them meaningful. A Unified Namespace built with FlowFuse and Node-RED unites the signal layer, context layer, and human decision layer so AI can provide real-time answers grounded in what is actually happening on the shop floor.","ySw3zH74xrF_Jar5vWu8Ee8gEiIZeNRoIRIFoYeZslI",{"id":5853,"title":5854,"authors":5855,"body":5857,"cta":3,"date":5990,"description":5854,"extension":207,"image":5991,"lastUpdated":3,"meta":5992,"navigation":216,"path":5997,"seo":5998,"sitemap":5999,"stem":6000,"subtitle":5854,"tags":6001,"tldr":3,"video":3,"__hash__":6002},"blog\u002Fblog\u002F2026\u002F01\u002Fflowfuse-release-2-26.md","FlowFuse 2.26: Bringing access-controls to your MCP nodes",[5856],"nick-oleary",{"type":12,"value":5858,"toc":5981},[5859,5862,5866,5874,5877,5880,5883,5886,5894,5898,5901,5915,5922,5931,5933,5940,5948,5951,5955,5959,5962,5965,5969],[15,5860,5861],{},"With the holiday break sitting in the middle of this release cycle, it's a smaller release than usual this month. But that hasn't stopped us continuing to make the FlowFuse Expert even more useful.",[39,5863,5865],{"id":5864},"role-based-access-control-for-your-mcp-tools","Role-based access control for your MCP tools",[15,5867,5868,5869,5873],{},"Following on the the introduction of ",[22,5870,5872],{"href":5871},"\u002Fchangelog\u002F2025\u002F12\u002Fff-expert-mcp-insights\u002F","FlowFuse Expert MCP-Powered Insights"," we have added annotations to the FlowFuse MCP nodes and linked them up with the FlowFuse roles.\nThis permits a level of control over who can access what. This is just a first step, we will be working in the area over the next few iterations.",[15,5875,5876],{},"The MCP nodes now allow you to set some standard annotations to give the platform a hint as to what type of action the node performs. This lets you separate tools that provide read-only information from those that make potentially-destructive changes.",[15,5878,5879],{},"Within the FlowFuse team, you can then use the granular RBAC feature to configure what users have access to the different types of node.",[15,5881,5882],{},"For example, Viewer role users can have access to read-only nodes, whilst Owners get to access the full range of tools. These roles can be customised for each Application within the team.",[15,5884,5885],{},"The annotations we apply are part of the MCP standard, so will be recognised by your own Agents and LLMs.",[15,5887,5888,5892],{},[30,5889],{"alt":5890,"dataZoomable":187,"src":5891},"MCP Server Tool Node with new annotations","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fmcp-annotations.png",[35,5893,5890],{},[39,5895,5897],{"id":5896},"integrating-flowfuse-expert-with-node-red","Integrating FlowFuse Expert with Node-RED",[15,5899,5900],{},"This release also brings some new abilities for the FlowFuse Expert to help you do things inside Node-RED itself.",[47,5902,5903,5909],{},[50,5904,5905,5908],{},[53,5906,5907],{},"Streamlined Node Installation:"," When the Expert suggests a node module, it can now automatically open the Palette Manager and filter for the correct package, leaving you just one click away from installation.",[50,5910,5911,5914],{},[53,5912,5913],{},"Direct Flow Imports:"," When the Expert provides demo flows, you no longer need to copy-paste JSON. The Expert can now inject those flows directly into your editor, ready for deployment.",[15,5916,5917,5918,5921],{},"Make sure you've updated the ",[76,5919,5920],{},"@flowfuse\u002Fnr-assistant"," module inside your instance to unlock these new capabilities.",[15,5923,5924,5928],{},[30,5925],{"alt":5926,"dataZoomable":187,"src":5927},"FlowFuse Expert Install Node","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Fff-expert-install-node.gif",[35,5929,5930],{},"FlowFuse Expert integration with the Palette Manager",[39,5932,380],{"id":379},[15,5934,5935,5936,474],{},"For a complete list of everything included in our 2.26 release, check out the ",[22,5937,473],{"href":5938,"rel":5939},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.26.0",[445],[15,5941,5942,5943,474],{},"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 ",[22,5944,5947],{"href":5945,"rel":5946},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Fissues\u002Fnew\u002Fchoose",[445],"issues on GitHub",[15,5949,5950],{},"Which of these new features are you most excited to try? Reach out on GitHub or social media!",[39,5952,5954],{"id":5953},"try-flowfuse","Try FlowFuse",[143,5956,5958],{"id":5957},"flowfuse-cloud","FlowFuse Cloud",[15,5960,5961],{},"The quickest way to get started is with FlowFuse Cloud.",[15,5963,5964],{},"[Get started for free]({% include \"sign-up-url.njk\" %}) and have your Node-RED instances running in the cloud within minutes.",[143,5966,5968],{"id":5967},"self-hosted","Self-Hosted",[15,5970,5971,5972,5976,5977,474],{},"Get FlowFuse running locally in under 30 minutes using ",[22,5973,5975],{"href":5974},"\u002Fdocs\u002Finstall\u002Fdocker\u002F","Docker"," or ",[22,5978,5980],{"href":5979},"\u002Fdocs\u002Finstall\u002Fkubernetes\u002F","Kubernetes",{"title":187,"searchDepth":188,"depth":188,"links":5982},[5983,5984,5985,5986],{"id":5864,"depth":191,"text":5865},{"id":5896,"depth":191,"text":5897},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":5987},[5988,5989],{"id":5957,"depth":196,"text":5958},{"id":5967,"depth":196,"text":5968},"2026-01-15","\u002Fblog\u002F2026\u002F01\u002Fimages\u002Frelease-2-26.png",{"excerpt":5993},{"type":12,"value":5994},[5995],[15,5996,5861],{},"\u002Fblog\u002F2026\u002F01\u002Fflowfuse-release-2-26",{"title":5854,"description":5854},{"loc":5997},"blog\u002F2026\u002F01\u002Fflowfuse-release-2-26",[223,529,530,224],"zrtE599ZcoDilcp6taK6keb6r7CGKPIP3kHkUNDyhPE",{"id":6004,"title":6005,"authors":6006,"body":6008,"cta":3,"date":6163,"description":6005,"extension":207,"image":6164,"lastUpdated":3,"meta":6165,"navigation":216,"path":6170,"seo":6171,"sitemap":6172,"stem":6173,"subtitle":6005,"tags":6174,"tldr":3,"video":6175,"__hash__":6176},"blog\u002Fblog\u002F2025\u002F11\u002Fflowfuse-release-2-24.md","FlowFuse 2.24: FlowFuse Expert in the Node-RED Editor, Scheduled Updates, Simpler Edge Device Addition, Store and Forward Blueprint, and what's next!",[6007],"greg-stoutenburg",{"type":12,"value":6009,"toc":6151},[6010,6013,6016,6027,6030,6033,6036,6040,6051,6054,6057,6060,6063,6067,6078,6081,6085,6088,6095,6100,6103,6107,6110,6113,6115,6122,6127,6135,6137,6139,6141,6143,6145],[15,6011,6012],{},"This release unlocks several new abilities for our users, speeding your development time, easing management of Node-RED instances, providing a smoother path to adding large numbers of devices, and more. Let's dig in.",[39,6014,714],{"id":6015},"flowfuse-expert",[15,6017,6018,6022],{},[30,6019],{"alt":6020,"src":6021},"Image of FlowFuse Expert UI","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Fff-expert-ui.png",[35,6023,6024],{},[1238,6025,6026],{},"FlowFuse Expert UI",[15,6028,6029],{},"The FlowFuse Expert used to live only on flowfuse.com, where you could use an LLM trained on FlowFuse and Node-RED documentation, and trained by the Node-RED experts at FlowFuse. Now, we've taken it a step further. The FlowFuse Expert is now available inside of the FlowFuse UI, and even in the Immersive Editor in Node-RED!",[15,6031,6032],{},"You are no longer limited in your interactions with the FlowFuse Expert by the location where you started your conversation. Keep the conversation going and rely on the Expert's advice right where you're building in Node-RED.",[15,6034,6035],{},"Even better, when you're working in the Immersive Editor, the Expert will recommend flows based on your inputs, and you can copy and paste them directly into the Node-RED Editor, once again saving you time and reducing the effort needed to develop in Node-RED.",[39,6037,6039],{"id":6038},"automatic-updates-of-instances","Automatic Updates of Instances",[15,6041,6042,6046],{},[30,6043],{"alt":6044,"src":6045},"Image of Scheduled Updates UI","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Fscheduler.png",[35,6047,6048],{},[1238,6049,6050],{},"Scheduled Updates Interface",[15,6052,6053],{},"When there are new updates to Node-RED, or the FlowFuse components, it has been a manual task for users to spot the update and trigger the upgrade to get the latest fixes and features applied to their instances.",[15,6055,6056],{},"We're here to make your tasks easier, not to give you more maintenance burdens. So with this release, we've introduced the ability to automatically apply any updates that are available to Node-RED or the FlowFuse stack around it. You get to pick a maintenance window during the week for when the updates should get applied.",[15,6058,6059],{},"For Starter tier teams, we will apply a default schedule for overnight at the weekend to minimise disruption. For Pro and Enterprise tiers, users can configure their own schedule via the Instance's Maintenance settings tab.",[15,6061,6062],{},"Keeping your software up to date is important, and this features gives you one less thing to worry about.",[39,6064,6066],{"id":6065},"simpler-edge-device-addition","Simpler Edge Device Addition",[15,6068,6069,6073],{},[30,6070],{"alt":6071,"src":6072},"Image of UI for provisioning token","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Ftoken.png",[35,6074,6075],{},[1238,6076,6077],{},"Provisioning Token Interface",[15,6079,6080],{},"Many of our customers make use of large fleets of edge devices. Through the use of provisioning tokens it's easy to get lots of devices setup quickly - but there were still some additional steps needed to get the devices properly named and organised. Now, when setting up a new device with a provisioning token, it is possible to name it at the same time, reducing the complexity of the workflow. We want you to be able to scale up your edge device count quickly and easily, and this represents a significant step in that direction.",[39,6082,6084],{"id":6083},"blueprint-store-and-forward","Blueprint: Store and Forward",[15,6086,6087],{},"When data acquisition and processing at the edge is mission critical, it is vital that data received at the edge can be stored and protected until it can be forwarded to its destination. Having heard from customers that an easy way to execute a store and forward structure is needed, we have created this Blueprint to speed your development of this data-preserving flow.",[15,6089,6090,6091,474],{},"Find out more on the ",[22,6092,6094],{"href":6093},"\u002Fblueprints\u002Fgetting-started\u002Fstore-and-forward\u002F","blueprint page",[6096,6097,6099],"h1",{"id":6098},"sneak-peek","Sneak Peek",[15,6101,6102],{},"FlowFuse MCP nodes allow you to surface information to an LLM to create custom AI agents. We're working on enabling FlowFuse to identify anything you have surfaced to an MCP, paving the way for creating massively powerful agents, enabled by everything you've connected to FlowFuse.",[39,6104,6106],{"id":6105},"flowfuse-expert-for-open-source-node-red","FlowFuse Expert for Open-Source Node-RED",[15,6108,6109],{},"FlowFuse Expert is our collection of AI-enhancements within the Node-RED editor - assisted creation of Function nodes, autocompleting flows and documentation generation amongst other features. Currently it's an exclusive feature of the FlowFuse platform, but we're hard at work to bring it to standalone Node-RED instances.",[15,6111,6112],{},"Coming soon you'll be able to experience the power of AI-enhanced development within Node-RED wherever it's running.",[39,6114,380],{"id":379},[15,6116,6117,6118,474],{},"For a complete list of everything included in our 2.24 release, check out the ",[22,6119,473],{"href":6120,"rel":6121},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.24.0",[445],[15,6123,5942,6124,474],{},[22,6125,5947],{"href":5945,"rel":6126},[445],[15,6128,6129,6130,6134],{},"Which of these new features are you most excited to try? Email me directly at ",[22,6131,6133],{"href":6132},"mailto:greg@flowfuse.com","greg@flowfuse.com"," - I'd love to hear from you!",[39,6136,5954],{"id":5953},[143,6138,5958],{"id":5957},[15,6140,5961],{},[15,6142,5964],{},[143,6144,5968],{"id":5967},[15,6146,5971,6147,5976,6149,474],{},[22,6148,5975],{"href":5974},[22,6150,5980],{"href":5979},{"title":187,"searchDepth":188,"depth":188,"links":6152},[6153,6154,6155,6156,6157,6158,6159],{"id":6015,"depth":191,"text":714},{"id":6038,"depth":191,"text":6039},{"id":6065,"depth":191,"text":6066},{"id":6083,"depth":191,"text":6084},{"id":6105,"depth":191,"text":6106},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":6160},[6161,6162],{"id":5957,"depth":196,"text":5958},{"id":5967,"depth":196,"text":5968},"2025-11-20","\u002Fblog\u002F2025\u002F11\u002Fimages\u002F2.24-release.png",{"excerpt":6166},{"type":12,"value":6167},[6168],[15,6169,6012],{},"\u002Fblog\u002F2025\u002F11\u002Fflowfuse-release-2-24",{"title":6005,"description":6005},{"loc":6170},"blog\u002F2025\u002F11\u002Fflowfuse-release-2-24",[223,529,530,224],"cYwa08W-2eI","YddyBwLVT6ybKb3SR4smjIY73mtdwJfjZnbA2tmBb4o",{"id":6178,"title":6179,"authors":6180,"body":6181,"cta":3,"date":6325,"description":6326,"extension":207,"image":6327,"lastUpdated":3,"meta":6328,"navigation":216,"path":6334,"seo":6335,"sitemap":6336,"stem":6337,"subtitle":6338,"tags":6339,"tldr":3,"video":6340,"__hash__":6341},"blog\u002Fblog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations.md","FlowFuse + LLM + MCP = Text Driven Operations",[538],{"type":12,"value":6182,"toc":6319},[6183,6186,6189,6193,6209,6216,6219,6223,6226,6233,6240,6254,6261,6268,6272,6275,6278,6281,6284,6288,6295,6301,6304,6307,6310],[15,6184,6185],{},"In industrial operations it's all about getting more out of the CAPEX already\nspent. Achieving higher efficiency means everyone needs to get data from a lot of\ndifferent machines, have an understanding how these machines form lines and fit\ntogether, and holistically understand these as a group of assets that collectively\ncan achieve more.",[15,6187,6188],{},"With the rapid adoption of Artificial Intelligence (AI), Model Context\nProtocol, low-code platforms it's clear that the future of operations is\nconversational.\nThe interface to machines is becoming plain text, allowing teams to obtain effects\nand scale operational excellence across entire business units simply by asking\nthe right questions.",[39,6190,6192],{"id":6191},"the-context-challenge","The Context Challenge",[15,6194,6195,6196,5976,6200,6204,6205,6208],{},"Data capture involves integrating various machine protocols (like\n",[22,6197,6199],{"href":6198},"\u002Fnode-red\u002Fprotocol\u002Fopc-ua\u002F","OPC-UA",[22,6201,6203],{"href":6202},"\u002Fnode-red\u002Fprotocol\u002Fmodbus\u002F","Modbus","),\ntransporting, combining, and visualizing the information. While low-code tools\nlike Node-RED have decreased the implementation time to mere hours, the full\nproblem isn't solved: what happens ",[35,6206,6207],{},"after"," the data is collected?",[15,6210,6211,6212,6215],{},"Often, raw sensor data, like pressure, voltage, and temperature readings, lacks\ncontext to immediately understand there's a problem worth solving. Even when a dashboard\nhas been built, spotting an issue (such as a high energy consumption on ",[76,6213,6214],{},"machine 1",")\ndoesn't inherently guide the operator on how to resolve it.\nFurthermore, data flow often involves a psychological hurdle, moving from areas\nwhere an engineer feels comfortable (perhaps the data storage side) to areas of\nless expertise (like machine protocols or physical voltage readings).",[15,6217,6218],{},"The goal is to asking high-level questions, such as:\n\"What changed in the energy consumption for machine 4?\".",[39,6220,6222],{"id":6221},"text-the-new-language-of-control","Text: The New Language of Control",[15,6224,6225],{},"Removing code, often a complex layer, can be now achieved because of Large Language\nModels (LLMs) and the Model Context Protocol (MCP).",[15,6227,6228,6229,6232],{},"LLMs, like ChatGPT, predict the next word in a sentence, allowing humans to\nquery systems using ",[53,6230,6231],{},"natural language"," rather than complex code.\nLLMs face fundamental limitations though: they are typically cloud-based, can be\nslow, and are trained at specific points in time, meaning they cannot inherently\nreact to real-time, event-based data or proprietary local context.",[15,6234,6235,6236,6239],{},"This is where the ",[53,6237,6238],{},"Model Context Protocol (MCP)"," steps in. The promise of\nMCP is to give models more context through an agreed-upon protocol.\nMCP allows operators to define exactly what read-only information (resources) or\nfunctionality (tools) they want to expose to the LLM.",[47,6241,6242,6248],{},[50,6243,6244,6247],{},[53,6245,6246],{},"Resources"," are read-only, like sensor readings, employee staff lists, vacation calendars, or specification sheets (e.g., upper and lower temperature limits).",[50,6249,6250,6253],{},[53,6251,6252],{},"Tools"," are functions that allow the LLM to perform an action or change a state in the physical world.",[15,6255,6256,6257,6260],{},"By feeding this context into an MCP server (such as the official ",[22,6258,6259],{"href":5753},"FlowFuse MCP node","),\nthe LLM transforms into a powerful operational partner.",[15,6262,6263,6264,6267],{},"For example, an operator can ask: \"Can you show me the last five temp sensor readings recorded?\".\nOnce the model identifies an anomaly, the operator can incorporate specifications and ask: \"Are any of these values outside of spec for upper temp or lower temp?\".\nIf a problem is confirmed, the system can use staff and location data to answer a pointed question like: ",[53,6265,6266],{},"\"Who are all the staff located nearest to the problem, and what is the quickest way to get there?\"",".\nThis capability quickly transforms complex data into actionable steps, finding the problem, comparing it to specs, finding the right person, and routing them to the site, all within a matter of minutes.",[39,6269,6271],{"id":6270},"orchestrating-effects-across-the-machine-fleet","Orchestrating Effects Across the Machine Fleet",[15,6273,6274],{},"The ability to propagate these text-driven decisions across many machines by the\nsame team is enabled by Node-RED serving as the essential integration layer.",[15,6276,6277],{},"FlowFuse, a major corporate sponsor of Node-RED, aims to fuse the digital realm\nwith machines and the shop floor for IoT use cases. Node-RED acts as the shell\nthat connects proprietary and legacy machine protocols (OT side) to the modern MCP structure.",[15,6279,6280],{},"If a manufacturing facility wants to allow an LLM to control a physical device,\nNode-RED can integrate the machine (e.g., a Siemens S7 stack light) and wrap the\ncontrol logic in an MCP tool. The LLM requests an action\n(e.g., \"turn the stack light green\"), the MCP tool sends the action through\nNode-RED's established adapters, and the action is executed.",[15,6282,6283],{},"This means that existing organizational logic and machine adapters, which have\nalready been integrated into Node-RED, can be instantly made LLM and AI ready.\nThis rapid adaptation allows a very broad spectrum of engineers to be applied to\nproblems, moving past relying on \"tribal knowledge\" held by a single expert.",[39,6285,6287],{"id":6286},"text-driven-playbooks-and-the-human-in-the-loop","Text-Driven Playbooks and the Human in the Loop",[15,6289,6290,6291,6294],{},"Looking ahead, this technology enables the creation of ",[53,6292,6293],{},"text-driven playbooks",".\nAn operator might input a natural language prompt:\n\"How do we optimize a certain procedure in the factory?\". The resulting\noperational procedure, driven by the LLM and executed via MCP tools, becomes a\ndocumented playbook. This system helps organizations achieve operational excellence\nby turning human text input into processes that the rest of the company can read,\nunderstand, and replicate.",[15,6296,6297,6298,474],{},"However, the industry must move cautiously. A critical element of the future of\noperations is the necessity of a ",[76,6299,6300],{},"human in the loop",[15,6302,6303],{},"AI is predictive and, in certain ways, random, meaning that if context slightly\nchanges, the output is not deterministic. When the consequences of an action are\nphysical or high-stakes\n(e.g., stopping a production line, or an irreversible action like boiling an egg),\nfull control should not be handed over to a non-deterministic system. The risk of\nan AI being \"as confident when they're wrong as when they're right\" necessitates human oversight.",[15,6305,6306],{},"For the near future (the next five years), the AI acts as a partner or a fault\npartner, making the uncomfortable aspects of complex flows more manageable. It is\ncurrently best applied in reversible or digital tasks, such as generating reports\nor triggering low-consequence actions like turning a stack light orange to signal\nan engineer. As trust grows and impacts iterate, AI will gain more influence and\ncontext, but the final, consequential decisions will remain with the human.",[15,6308,6309],{},"The combination of LLMs, MCP, and Node-RED provides operators with super powers.\nThe operational floor is transforming from a place where experts write complex\ncode for singular machines, to a conversational environment where high-level, natural\nlanguage queries drive intelligent, scalable actions across the entire enterprise.",[15,6311,6312,6313,6318],{},"Ready to experience text-driven operations in your own facility?\n[Try FlowFuse for free]({% include \"sign-up-url.njk\" %}) or request a personalized demo to see how LLM-powered automation can transform your industrial processes.\n",[22,6314,6317],{"href":6315,"rel":6316},"https:\u002F\u002Fflowfuse.com\u002Fcontact-us\u002F",[445],"Contact us today"," or sign up for our upcoming webinar to stay ahead in the Industry 4.0 revolution.",{"title":187,"searchDepth":188,"depth":188,"links":6320},[6321,6322,6323,6324],{"id":6191,"depth":191,"text":6192},{"id":6221,"depth":191,"text":6222},{"id":6270,"depth":191,"text":6271},{"id":6286,"depth":191,"text":6287},"2025-11-12","Discover how FlowFuse combines LLMs and Model Context Protocol (MCP) with Node-RED to enable text-driven operations, transforming industrial data into actionable insights through natural language queries.","\u002Fblog\u002F2025\u002F11\u002Fimages\u002Fflowfuse+llm+mcp-equals-text-driven-operations.png",{"keywords":6329,"excerpt":6330},"MCP, Node-RED MCP, LLM",{"type":12,"value":6331},[6332],[15,6333,6185],{},"\u002Fblog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations",{"title":6179,"description":6326},{"loc":6334},"blog\u002F2025\u002F11\u002Fflowfuse+llm+mcp-equals-text-driven-operations","Moving from a Code driven interface to LLMs interpreting humans.",[223,437,224],"vaesYCyEESY","Svo_s3rUH5XV8xLsr6m6fXpqSh5v2pwBKLl1geXGnaw",{"id":6343,"title":6344,"authors":6345,"body":6346,"cta":3,"date":6523,"description":6524,"extension":207,"image":6525,"lastUpdated":3,"meta":6526,"navigation":216,"path":6531,"seo":6532,"sitemap":6533,"stem":6534,"subtitle":6524,"tags":6535,"tldr":3,"video":3,"__hash__":6536},"blog\u002Fblog\u002F2025\u002F10\u002Fflowfuse-release-2-23.md","FlowFuse 2.23: MCP and ONNX nodes, FlowFuse AI Expert on the homepage, Application-level Permission Control, FlowFuse Expert for Self-Hosted, and more!",[6007],{"type":12,"value":6347,"toc":6509},[6348,6351,6355,6366,6375,6379,6382,6391,6395,6406,6409,6412,6414,6425,6434,6441,6445,6452,6456,6459,6461,6464,6468,6475,6477,6484,6489,6493,6495,6497,6499,6501,6503],[15,6349,6350],{},"In this exciting release, we've shipped several features that accelerate development in Node-RED using AI, enable creation of AI agents using new Model Context Protocol nodes, provide application-level access controls for much more sophisticated user permissions management, and put an expert flow creator right on our homepage. It's a big one! Let's have a look.",[39,6352,6354],{"id":6353},"mcp-nodes","MCP Nodes",[15,6356,6357,6361],{},[30,6358],{"alt":6359,"src":6360},"Image of MCP nodes","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fmcp-nodes.png",[35,6362,6363],{},[1238,6364,6365],{},"MCP nodes in the Node-RED palette, ready for use",[15,6367,6368,6369,6374],{},"With this release, you can now create a Model Context Protocol server using Node-RED. The new MCP nodes enable the creation of a MCP server so that you can create AI agents that will rely on the exact data that you want to surface to an LLM. As AI services rely upon data presented to them, whether it's anything available on the world wide web (as with LLMs generically) or more focused information, our MCP nodes provide the ability to expose specific resources so that an AI service will rely upon the right data without making all of your information public. It's a really exciting development. Check out ",[22,6370,6373],{"href":6371,"rel":6372},"https:\u002F\u002Fflowfuse.com\u002Fchangelog\u002F2025\u002F10\u002Fmcp-nodes\u002F",[445],"the Changelog entry"," for more.",[39,6376,6378],{"id":6377},"flowfuse-ai-nodes","FlowFuse AI Nodes",[15,6380,6381],{},"While MCP makes it possible to create AI agents to rely on the data you have chosen, the new FlowFuse AI nodes allow you to connect AI models of your choosing--including ones that you have trained yourself--to Node-RED to create any workflow you like. The ONNX (Open Neural Network Exchange) format lets you connect a model for whatever purpose you have in mind. This package ships with nodes for running your own custom-trained model, for classifying images, for object detection, and for image depth estimation. This is a huge step in the direction of fully-controlled AI automation inside of Node-RED on FlowFuse.",[15,6383,6384,6385,6390],{},"For self-hosted customers, once you've upgraded, contact ",[22,6386,6389],{"href":6387,"rel":6388},"https:\u002F\u002Fflowfuse.com\u002Fsupport",[445],"FlowFuse Support"," to get access to these exciting new nodes.",[39,6392,6394],{"id":6393},"application-level-role-based-access-control","Application-Level Role-Based Access Control",[15,6396,6397,6401],{},[30,6398],{"alt":6399,"src":6400},"Image of application-level permissions","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Frbac2.png",[35,6402,6403],{},[1238,6404,6405],{},"Control permissions at the application level",[15,6407,6408],{},"One of the key features of FlowFuse is the ability to manage Node-RED applications by choosing who gets access to what. Up to now, team owners had to configure permissions at the team level. But what if there are members of your team that should have write permissions to one Node-RED instance, but only viewer access to another? You would have to put those instances on different teams and configure permissions there. With many instances and applications, this could be a headache.",[15,6410,6411],{},"We've solved it. You can now manage permissions at the Application level. Now one and the same team can have users who can access some applications with one level of permissions, and and another application with a different level. We've heard your feedback on this need and are happy for you to give it a try!",[39,6413,714],{"id":6015},[15,6415,6416,6420],{},[30,6417],{"alt":6418,"src":6419},"Image of FlowFuse Expert","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fexpert.png",[35,6421,6422],{},[1238,6423,6424],{},"The FlowFuse Expert gives step-by-step instructions for building flows",[15,6426,6427,6428,6433],{},"You can now get complete instructions for building a Node-RED flow on ",[22,6429,6432],{"href":6430,"rel":6431},"https:\u002F\u002Fflowfuse.com",[445],"flowfuse.com"," using the FlowFuse Expert! This newest feature in the FlowFuse AI toolkit provides detailed guidance on creating Node-RED flows for any purpose you have in mind.",[15,6435,6436,6437,6440],{},"Head over to ",[22,6438,6432],{"href":6430,"rel":6439},[445]," to check it out! We have big plans in mind for this feature, but can't say much yet. For now, it's enough to say that low code development is headed toward a whole new level with FlowFuse.",[39,6442,6444],{"id":6443},"flowfuse-expert-for-self-hosted-deployments","FlowFuse Expert for Self Hosted Deployments",[15,6446,6447,6448,6451],{},"Speaking of AI assistance, self-hosted FlowFuse deployments have until now not had access to the FlowFuse Expert inside of Node-RED, helping to complete flows using natural language for explanation of flows, creation of Dashboard, Function, and Tables nodes, and Snapshot descriptions. These had been available only to FlowFuse Cloud customers. I'm happy to say that our many self-hosted customers now (finally) can get access to this tool, which helps speed flow creation exponentially. Contact ",[22,6449,6389],{"href":6387,"rel":6450},[445]," to help get you setup.",[39,6453,6455],{"id":6454},"import-json-at-instance-creation","Import JSON at Instance Creation",[15,6457,6458],{},"Developers want the fastest way to spin up a functional Node-RED instance, and we've taken another step in that direction by offering the option of uploading a JSON file during the instance creation workflow.",[6096,6460,6099],{"id":6098},[15,6462,6463],{},"What if you could surface your FlowFuse Tables data outside of the FlowFuse environment, for broader consumption as you see fit? Or use data coming into Node-RED to predict maintainance needs? We have exciting things on the way for you!",[39,6465,6467],{"id":6466},"dont-miss-node-red-con-2025","Don't Miss Node-RED Con 2025!",[15,6469,6470,6471],{},"While not exactly a release item, I'd be remiss if I failed to mention Node-RED Conference 2025! We've got a great lineup of speakers who will cover a broad and deep variety of topics on the use of Node-RED. Check out the website and register here: ",[22,6472,6473],{"href":6473,"rel":6474},"https:\u002F\u002Fnrcon.nodered.org\u002F.",[445],[39,6476,380],{"id":379},[15,6478,6479,6480,474],{},"For a complete list of everything included in our 2.23 release, check out the ",[22,6481,473],{"href":6482,"rel":6483},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.23.0",[445],[15,6485,5942,6486,474],{},[22,6487,5947],{"href":5945,"rel":6488},[445],[15,6490,6129,6491,6134],{},[22,6492,6133],{"href":6132},[39,6494,5954],{"id":5953},[143,6496,5958],{"id":5957},[15,6498,5961],{},[15,6500,5964],{},[143,6502,5968],{"id":5967},[15,6504,5971,6505,5976,6507,474],{},[22,6506,5975],{"href":5974},[22,6508,5980],{"href":5979},{"title":187,"searchDepth":188,"depth":188,"links":6510},[6511,6512,6513,6514,6515,6516,6517,6518,6519],{"id":6353,"depth":191,"text":6354},{"id":6377,"depth":191,"text":6378},{"id":6393,"depth":191,"text":6394},{"id":6015,"depth":191,"text":714},{"id":6443,"depth":191,"text":6444},{"id":6454,"depth":191,"text":6455},{"id":6466,"depth":191,"text":6467},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":6520},[6521,6522],{"id":5957,"depth":196,"text":5958},{"id":5967,"depth":196,"text":5968},"2025-10-23","MCP and ONNX nodes, FlowFuse AI Expert on the homepage, Application-level Permission Control, FlowFuse Expert for Self-Hosted, and more!","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fflowfuse-release-2-23.png",{"excerpt":6527},{"type":12,"value":6528},[6529],[15,6530,6350],{},"\u002Fblog\u002F2025\u002F10\u002Fflowfuse-release-2-23",{"title":6344,"description":6524},{"loc":6531},"blog\u002F2025\u002F10\u002Fflowfuse-release-2-23",[223,529,530,224],"f9vf2PTx0DLYtfxgXSj7erwPgnjWvzLxBdylRFG0LE8",{"id":6538,"title":6539,"authors":6540,"body":6541,"cta":3,"date":6621,"description":6622,"extension":207,"image":6623,"lastUpdated":3,"meta":6624,"navigation":216,"path":6629,"seo":6630,"sitemap":6631,"stem":6632,"subtitle":6622,"tags":6633,"tldr":3,"video":3,"__hash__":6635},"blog\u002Fblog\u002F2025\u002F10\u002Fai-on-flowfuse.md","MCP and Custom AI Models on FlowFuse!",[6007],{"type":12,"value":6542,"toc":6614},[6543,6546,6550,6553,6556,6559,6563,6566,6569,6572,6575,6579,6582,6591,6595,6598,6601,6604,6608,6611],[15,6544,6545],{},"We have a VERY exciting announcement today: you can now build an MCP server and upload custom-trained AI models to FlowFuse!",[39,6547,6549],{"id":6548},"ai-on-flowfuse","AI on FlowFuse",[15,6551,6552],{},"Node-RED is already the most capable and flexible low-code development environment out there. FlowFuse makes it secure, robust, and scalable.",[15,6554,6555],{},"As of today, you can now use FlowFuse to create your own MCP server and use custom-trained AI models that you've built for any application.",[15,6557,6558],{},"Build an MCP server to create an AI agent that will do whatever you've design it to do. And add a training model that you've built using your own training data, so data processing works exactly the way you want it to.",[39,6560,6562],{"id":6561},"model-context-protocol","Model Context Protocol",[15,6564,6565],{},"LLMs were trained by scraping data from the internet to build data models that can complete a task (like answering a question or writing a blog article -- but not this one!) given some input prompt. When you rely on an LLM to answer questions that are very general or perform operations that are rather straightforward, LLMs can generally do so with ease.",[15,6567,6568],{},"However, if you want to do something more sophisticated, or create an AI agent that will rely on specific data, that data needs to be presented to the AI somehow. MCP is what enables that.",[15,6570,6571],{},"Instead of relying just on LLMs trained from scraping the entire internet, the new MCP nodes enable you to create your own MCP servers, so you can present information to an AI tool, putting much more power and control in your hands as a developer.",[15,6573,6574],{},"You are now able to create your own, custom AI agent using FlowFuse.",[39,6576,6578],{"id":6577},"custom-data-models","Custom Data Models",[15,6580,6581],{},"When creating an AI agent or getting AI assistance with some task, one component is the data that is surfaced to the model. That part is handled by MCP. Another part is how the model interacts with that data. That part is handled with our new AI nodes.",[15,6583,6584,6585,6590],{},"Instead of relying on a standard LLM, even one that has been set up to connect with an MCP server, it is possible to train the model itself. The new AI nodes allow you to train a custom model, put it in ",[22,6586,6589],{"href":6587,"rel":6588},"https:\u002F\u002Fonnx.ai",[445],"ONNX"," format, and connect it to Node-RED, where it can be deployed to run any operation you wish with your new, personally-trained AI.",[39,6592,6594],{"id":6593},"the-sky-is-the-limit","The Sky Is the Limit",[15,6596,6597],{},"The flexibility of Node-RED, the reliability of FlowFuse, and the customizability enabled by these MCP and AI nodes means you can build just about any AI application you wish!",[15,6599,6600],{},"And this week, we're going to tell you all about it. Stay tuned for a demo or three, some tutorials, and overall plenty of instructional content goodness that will demonstrate the power that is now available with these AI releases!",[15,6602,6603],{},"While we're at it, we are also going to unveil a new feature right on our home page: a trained FlowFuse Expert (AI) that will teach you how to build applications in FlowFuse and Node-RED!",[39,6605,6607],{"id":6606},"try-it-now","Try it Now",[15,6609,6610],{},"Ready to try out these new nodes? If you're new to FlowFuse, [create a trial team]({% include \"sign-up-url.njk\" %}). Then, head to Manage Palette (in the hamburger menu on the right side of the Node-RED editor), click Install, and in the dropdown menu, choose FlowFuse Nodes to see the catalog of nodes that are exclusive to FlowFuse customers. From here, you can install both the MCP nodes and AI ONNX nodes.",[15,6612,6613],{},"If you're an existing FlowFuse user, begin by restarting the Node-RED instance where you want to use the nodes, then follow the same instructions as above.",{"title":187,"searchDepth":188,"depth":188,"links":6615},[6616,6617,6618,6619,6620],{"id":6548,"depth":191,"text":6549},{"id":6561,"depth":191,"text":6562},{"id":6577,"depth":191,"text":6578},{"id":6593,"depth":191,"text":6594},{"id":6606,"depth":191,"text":6607},"2025-10-13","Create your own AI agents and deploy trained models in Node-RED","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fai-on-flowfuse.png",{"keywords":3,"excerpt":6625},{"type":12,"value":6626},[6627],[15,6628,6545],{},"\u002Fblog\u002F2025\u002F10\u002Fai-on-flowfuse",{"title":6539,"description":6622},{"loc":6629},"blog\u002F2025\u002F10\u002Fai-on-flowfuse",[223,437,6634,224],"post","IKcKTFvRCZsyI7kF7G2ox4IYygiyIOnCWwPb791AmQ4",{"id":6637,"title":6638,"authors":6639,"body":6641,"cta":3,"date":8195,"description":8196,"extension":207,"image":8197,"lastUpdated":3,"meta":8198,"navigation":216,"path":8204,"seo":8205,"sitemap":8206,"stem":8207,"subtitle":8208,"tags":8209,"tldr":3,"video":3,"__hash__":8210},"blog\u002Fblog\u002F2025\u002F10\u002Fcustom-onnx-model.md","Deploy Custom-Trained AI Models: Using ONNX with Node-RED and FlowFuse",[6640],"stephen-mclaughlin",{"type":12,"value":6642,"toc":8175},[6643,6646,6650,6653,6661,6665,6668,6672,6675,6686,6689,6693,6698,6705,6711,6716,6725,6746,6755,6759,6770,6817,6821,6824,6838,6841,6858,6861,6875,6880,6883,6922,6925,6972,6979,6997,7001,7014,7205,7212,7324,7328,7331,7337,7355,7358,7917,7921,7924,7935,7938,7990,7997,8001,8078,8086,8094,8098,8102,8105,8117,8124,8155,8158,8172],[15,6644,6645],{},"FlowFuse is introducing a new set of AI nodes to make it easier than ever to integrate AI and machine learning into your Node-RED workflows.\nIn this guide, you will learn how to train an image classifier model, and use it with the new FlowFuse AI Nodes to recognise your own products, components - or anything else you can imagine.",[143,6647,6649],{"id":6648},"introduction","Introduction",[15,6651,6652],{},"In this article, we will be building a PyTorch-based image classification model to identify fruit types (apple, kiwi, mango) using a dataset of labelled images.\nOf course, you would typically be classifying your own things like your company widgets and products, but for the sake of learning the process, we will be using images of fruit.\nOnce the model is trained, it is exported to the ONNX format, it is then ready for use with the new FlowFuse AI nodes.",[15,6654,6655,6656,474],{},"Note: The code and sample dataset used in this tutorial can be downloaded from ",[22,6657,6660],{"href":6658,"rel":6659},"https:\u002F\u002Fwebsite-data.s3.eu-west-1.amazonaws.com\u002F2025-10-onnx-model-training-dataset.zip",[445],"this link",[143,6662,6664],{"id":6663},"some-background-first","Some background first",[15,6666,6667],{},"The process we will use is commonly referred to as \"transfer learning\". This is where you take a pre-trained model and fine-tune it on your own dataset.\nThis is a common approach in deep learning as it allows us to leverage the knowledge learned by the pre-trained model and adapt it to our specific task with a smaller dataset.  For reference, this tutorial will use ResNet-18 which is an 18-layer Residual Network (ResNet), a convolutional neural network (CNN) architecture that uses \"skip connections\" to help train very deep networks by addressing the vanishing gradient problem. Pre-trained ResNet-18 models are often trained on the ImageNet dataset and are widely used for image classification of 1000 categories.",[143,6669,6671],{"id":6670},"overview-of-operations","Overview of operations",[15,6673,6674],{},"The 3 main steps to achieve this involves:",[3084,6676,6677,6680,6683],{},[50,6678,6679],{},"Setting up a Python environment with PyTorch, TorchVision, ONNX, and ONNX Runtime.",[50,6681,6682],{},"Organizing your dataset into train, validation, and test folders for each class.",[50,6684,6685],{},"Perform \"transfer learning\" to fine-tune the model against your images & generate the ONNX model.",[15,6687,6688],{},"Let's get started...",[143,6690,6692],{"id":6691},"setup-the-environment","Setup the environment",[6694,6695,6697],"h4",{"id":6696},"pre-requisites","Pre-requisites",[15,6699,6700,6701,6704],{},"This tutorial was tested on Ubuntu using Python 3 and ",[76,6702,6703],{},"pyenv"," for environment management.",[15,6706,6707,6708,6710],{},"For the sake of brevity, from this point forward, the tutorial will assume you are using a debian based operating system and ",[76,6709,6703],{},".\nInstructions will need to be adapted if you are using something else.",[6712,6713,6715],"h5",{"id":6714},"python-tools","Python tools",[15,6717,6718,6719,1423,6721,6724],{},"Ensure you have ",[76,6720,6703],{},[76,6722,6723],{},"pyenv-virtualenv"," installed.",[1195,6726,6728],{"className":1488,"code":6727,"language":1490,"meta":187,"style":187},"pyenv --version\npyenv virtualenv --version\n",[76,6729,6730,6737],{"__ignoreMap":187},[1238,6731,6732,6734],{"class":1240,"line":1241},[1238,6733,6703],{"class":1497},[1238,6735,6736],{"class":1266}," --version\n",[1238,6738,6739,6741,6744],{"class":1240,"line":191},[1238,6740,6703],{"class":1497},[1238,6742,6743],{"class":1266}," virtualenv",[1238,6745,6736],{"class":1266},[15,6747,6748,6749,6754],{},"If you don't have them installed, this ",[22,6750,6753],{"href":6751,"rel":6752},"https:\u002F\u002Fmedium.com\u002F@aashari\u002Feasy-to-follow-guide-of-how-to-install-pyenv-on-ubuntu-a3730af8d7f0",[445],"Medium article"," worked well in our case.",[6712,6756,6758],{"id":6757},"sub-dependencies","Sub dependencies",[15,6760,6761,6762,6765,6766,6769],{},"During setup and testing, my installation failed at the last step due to missing ",[76,6763,6764],{},"bz2"," support (a TorchVision dependency).\nIf you encounter this, you would need to install ",[76,6767,6768],{},"libbz2"," then you would need to rebuild your python environment.\nTo save time, I recommend that you perform the steps below now to ensure the dependencies are installed and avoid the mis-step.",[1195,6771,6773],{"className":1488,"code":6772,"language":1490,"meta":187,"style":187},"sudo apt update\nsudo apt install -y libbz2-dev liblzma-dev libsqlite3-dev libssl-dev zlib1g-dev libffi-dev build-essential\n",[76,6774,6775,6785],{"__ignoreMap":187},[1238,6776,6777,6779,6782],{"class":1240,"line":1241},[1238,6778,3133],{"class":1497},[1238,6780,6781],{"class":1266}," apt",[1238,6783,6784],{"class":1266}," update\n",[1238,6786,6787,6789,6791,6793,6796,6799,6802,6805,6808,6811,6814],{"class":1240,"line":191},[1238,6788,3133],{"class":1497},[1238,6790,6781],{"class":1266},[1238,6792,1547],{"class":1266},[1238,6794,6795],{"class":1266}," -y",[1238,6797,6798],{"class":1266}," libbz2-dev",[1238,6800,6801],{"class":1266}," liblzma-dev",[1238,6803,6804],{"class":1266}," libsqlite3-dev",[1238,6806,6807],{"class":1266}," libssl-dev",[1238,6809,6810],{"class":1266}," zlib1g-dev",[1238,6812,6813],{"class":1266}," libffi-dev",[1238,6815,6816],{"class":1266}," build-essential\n",[6694,6818,6820],{"id":6819},"virtual-environment-setup","Virtual Environment Setup",[15,6822,6823],{},"Install python 3.10.14 (or any version compatible with pytorch and onnx):",[1195,6825,6827],{"className":1488,"code":6826,"language":1490,"meta":187,"style":187},"pyenv install 3.10.14\n",[76,6828,6829],{"__ignoreMap":187},[1238,6830,6831,6833,6835],{"class":1240,"line":1241},[1238,6832,6703],{"class":1497},[1238,6834,1547],{"class":1266},[1238,6836,6837],{"class":1286}," 3.10.14\n",[15,6839,6840],{},"Create a new virtual environment:",[1195,6842,6844],{"className":1488,"code":6843,"language":1490,"meta":187,"style":187},"pyenv virtualenv 3.10.14 venv_py3_10_14_pytorch\n",[76,6845,6846],{"__ignoreMap":187},[1238,6847,6848,6850,6852,6855],{"class":1240,"line":1241},[1238,6849,6703],{"class":1497},[1238,6851,6743],{"class":1266},[1238,6853,6854],{"class":1286}," 3.10.14",[1238,6856,6857],{"class":1266}," venv_py3_10_14_pytorch\n",[15,6859,6860],{},"Activate the virtual environment:",[1195,6862,6864],{"className":1488,"code":6863,"language":1490,"meta":187,"style":187},"pyenv activate venv_py3_10_14_pytorch\n",[76,6865,6866],{"__ignoreMap":187},[1238,6867,6868,6870,6873],{"class":1240,"line":1241},[1238,6869,6703],{"class":1497},[1238,6871,6872],{"class":1266}," activate",[1238,6874,6857],{"class":1266},[15,6876,6877],{},[35,6878,6879],{},"NOTE: Depending on your shell, your commandline may become decorated with the name of the virtual environment.",[15,6881,6882],{},"Install the required packages:",[1195,6884,6886],{"className":1488,"code":6885,"language":1490,"meta":187,"style":187},"pip install --upgrade pip\npip install torch torchvision onnx onnxruntime matplotlib numpy\n",[76,6887,6888,6901],{"__ignoreMap":187},[1238,6889,6890,6893,6895,6898],{"class":1240,"line":1241},[1238,6891,6892],{"class":1497},"pip",[1238,6894,1547],{"class":1266},[1238,6896,6897],{"class":1266}," --upgrade",[1238,6899,6900],{"class":1266}," pip\n",[1238,6902,6903,6905,6907,6909,6912,6914,6916,6919],{"class":1240,"line":191},[1238,6904,6892],{"class":1497},[1238,6906,1547],{"class":1266},[1238,6908,1556],{"class":1266},[1238,6910,6911],{"class":1266}," torchvision",[1238,6913,1559],{"class":1266},[1238,6915,1562],{"class":1266},[1238,6917,6918],{"class":1266}," matplotlib",[1238,6920,6921],{"class":1266}," numpy\n",[15,6923,6924],{},"Create a working directory",[1195,6926,6928],{"className":1488,"code":6927,"language":1490,"meta":187,"style":187},"mkdir ~\u002Fmy-py-projects\ncd ~\u002Fmy-py-projects\nmkdir pytorch-onnx\ncd pytorch-onnx\n# Associate this directory with the virtual env we created earlier\npyenv local venv_py3_10_14_pytorch\n",[76,6929,6930,6938,6945,6952,6958,6963],{"__ignoreMap":187},[1238,6931,6932,6935],{"class":1240,"line":1241},[1238,6933,6934],{"class":1497},"mkdir",[1238,6936,6937],{"class":1266}," ~\u002Fmy-py-projects\n",[1238,6939,6940,6943],{"class":1240,"line":191},[1238,6941,6942],{"class":1512},"cd",[1238,6944,6937],{"class":1266},[1238,6946,6947,6949],{"class":1240,"line":196},[1238,6948,6934],{"class":1497},[1238,6950,6951],{"class":1266}," pytorch-onnx\n",[1238,6953,6954,6956],{"class":1240,"line":188},[1238,6955,6942],{"class":1512},[1238,6957,6951],{"class":1266},[1238,6959,6960],{"class":1240,"line":1334},[1238,6961,6962],{"class":4041},"# Associate this directory with the virtual env we created earlier\n",[1238,6964,6965,6967,6970],{"class":1240,"line":1372},[1238,6966,6703],{"class":1497},[1238,6968,6969],{"class":1266}," local",[1238,6971,6857],{"class":1266},[15,6973,6974,6975,6978],{},"(Optional) Create a ",[76,6976,6977],{},"requirements.txt"," file to document the packages used in this project:",[1195,6980,6982],{"className":1488,"code":6981,"language":1490,"meta":187,"style":187},"pip freeze > requirements.txt\n",[76,6983,6984],{"__ignoreMap":187},[1238,6985,6986,6988,6991,6994],{"class":1240,"line":1241},[1238,6987,6892],{"class":1497},[1238,6989,6990],{"class":1266}," freeze",[1238,6992,6993],{"class":1244}," >",[1238,6995,6996],{"class":1266}," requirements.txt\n",[143,6998,7000],{"id":6999},"organizing-your-dataset","Organizing your dataset",[15,7002,7003,7004,5976,7009,7013],{},"For this example, I have created a simple dataset of images of apples, kiwis, and mangos.\nYou can use your own dataset or download a dataset from the internet (e.g. ",[22,7005,7008],{"href":7006,"rel":7007},"https:\u002F\u002Fwww.kaggle.com\u002Fdatasets\u002F",[445],"this one",[22,7010,7008],{"href":7011,"rel":7012},"https:\u002F\u002Fimages.cv\u002Fsearch-labeled-image-dataset",[445],").\nJust make sure to organize the images in the following structure:",[1195,7015,7017],{"className":1488,"code":7016,"language":1490,"meta":187,"style":187},"data\u002F\n    train\u002F\n        apples\u002F\n            apple1.jpg\n            apple2.jpg\n            ...\n        kiwis\u002F\n            kiwi1.jpg\n            kiwi2.jpg\n            ...\n        mangos\u002F\n            mango1.jpg\n            mango2.jpg\n            ...\n    val\u002F\n        apples\u002F\n            apple3.jpg\n            apple4.jpg\n            ...\n        kiwis\u002F\n            kiwi3.jpg\n            kiwi4.jpg\n            ...\n        mangos\u002F\n            mango3.jpg\n            mango4.jpg\n            ...\n    test\u002F\n        apples\u002F\n            apple5.jpg\n            apple6.jpg\n            ...\n        kiwis\u002F\n            kiwi5.jpg\n            kiwi6.jpg\n            ...\n        mangos\u002F\n            mango5.jpg\n            mango6.jpg\n            ...\n",[76,7018,7019,7024,7029,7034,7039,7044,7049,7054,7059,7064,7068,7073,7078,7083,7087,7092,7096,7101,7106,7110,7114,7119,7124,7128,7132,7137,7142,7146,7151,7155,7160,7165,7169,7173,7178,7183,7187,7191,7196,7201],{"__ignoreMap":187},[1238,7020,7021],{"class":1240,"line":1241},[1238,7022,7023],{"class":1497},"data\u002F\n",[1238,7025,7026],{"class":1240,"line":191},[1238,7027,7028],{"class":1497},"    train\u002F\n",[1238,7030,7031],{"class":1240,"line":196},[1238,7032,7033],{"class":1497},"        apples\u002F\n",[1238,7035,7036],{"class":1240,"line":188},[1238,7037,7038],{"class":1497},"            apple1.jpg\n",[1238,7040,7041],{"class":1240,"line":1334},[1238,7042,7043],{"class":1497},"            apple2.jpg\n",[1238,7045,7046],{"class":1240,"line":1372},[1238,7047,7048],{"class":1512},"            ...\n",[1238,7050,7051],{"class":1240,"line":1411},[1238,7052,7053],{"class":1497},"        kiwis\u002F\n",[1238,7055,7056],{"class":1240,"line":1807},[1238,7057,7058],{"class":1497},"            kiwi1.jpg\n",[1238,7060,7061],{"class":1240,"line":1813},[1238,7062,7063],{"class":1497},"            kiwi2.jpg\n",[1238,7065,7066],{"class":1240,"line":1819},[1238,7067,7048],{"class":1512},[1238,7069,7070],{"class":1240,"line":1825},[1238,7071,7072],{"class":1497},"        mangos\u002F\n",[1238,7074,7075],{"class":1240,"line":1831},[1238,7076,7077],{"class":1497},"            mango1.jpg\n",[1238,7079,7080],{"class":1240,"line":1837},[1238,7081,7082],{"class":1497},"            mango2.jpg\n",[1238,7084,7085],{"class":1240,"line":1843},[1238,7086,7048],{"class":1512},[1238,7088,7089],{"class":1240,"line":1849},[1238,7090,7091],{"class":1497},"    val\u002F\n",[1238,7093,7094],{"class":1240,"line":1855},[1238,7095,7033],{"class":1497},[1238,7097,7098],{"class":1240,"line":1860},[1238,7099,7100],{"class":1497},"            apple3.jpg\n",[1238,7102,7103],{"class":1240,"line":1866},[1238,7104,7105],{"class":1497},"            apple4.jpg\n",[1238,7107,7108],{"class":1240,"line":1872},[1238,7109,7048],{"class":1512},[1238,7111,7112],{"class":1240,"line":1878},[1238,7113,7053],{"class":1497},[1238,7115,7116],{"class":1240,"line":1884},[1238,7117,7118],{"class":1497},"            kiwi3.jpg\n",[1238,7120,7121],{"class":1240,"line":1890},[1238,7122,7123],{"class":1497},"            kiwi4.jpg\n",[1238,7125,7126],{"class":1240,"line":1896},[1238,7127,7048],{"class":1512},[1238,7129,7130],{"class":1240,"line":1902},[1238,7131,7072],{"class":1497},[1238,7133,7134],{"class":1240,"line":1907},[1238,7135,7136],{"class":1497},"            mango3.jpg\n",[1238,7138,7139],{"class":1240,"line":1913},[1238,7140,7141],{"class":1497},"            mango4.jpg\n",[1238,7143,7144],{"class":1240,"line":1919},[1238,7145,7048],{"class":1512},[1238,7147,7148],{"class":1240,"line":1925},[1238,7149,7150],{"class":1497},"    test\u002F\n",[1238,7152,7153],{"class":1240,"line":1931},[1238,7154,7033],{"class":1497},[1238,7156,7157],{"class":1240,"line":1937},[1238,7158,7159],{"class":1497},"            apple5.jpg\n",[1238,7161,7162],{"class":1240,"line":1943},[1238,7163,7164],{"class":1497},"            apple6.jpg\n",[1238,7166,7167],{"class":1240,"line":1949},[1238,7168,7048],{"class":1512},[1238,7170,7171],{"class":1240,"line":1955},[1238,7172,7053],{"class":1497},[1238,7174,7175],{"class":1240,"line":1960},[1238,7176,7177],{"class":1497},"            kiwi5.jpg\n",[1238,7179,7180],{"class":1240,"line":1966},[1238,7181,7182],{"class":1497},"            kiwi6.jpg\n",[1238,7184,7185],{"class":1240,"line":1972},[1238,7186,7048],{"class":1512},[1238,7188,7189],{"class":1240,"line":1977},[1238,7190,7072],{"class":1497},[1238,7192,7193],{"class":1240,"line":1983},[1238,7194,7195],{"class":1497},"            mango5.jpg\n",[1238,7197,7198],{"class":1240,"line":1989},[1238,7199,7200],{"class":1497},"            mango6.jpg\n",[1238,7202,7203],{"class":1240,"line":1995},[1238,7204,7048],{"class":1512},[15,7206,7207,7208,7211],{},"Now, inside ",[76,7209,7210],{},"~\u002Fmy-py-projects\u002Fpytorch-onnx\u002F"," you should have:",[1195,7213,7215],{"className":1488,"code":7214,"language":1490,"meta":187,"style":187},"pytorch-onnx\u002F\n│\n├── data\u002F\n│   ├── train\u002F\n│   │   ├── apples\u002F\n│   │   ├── kiwis\u002F\n│   │   └── mangos\u002F\n│   ├── val\u002F\n│   └── test\u002F\n│\n├── fruit_classifier.py   # we will create this shortly\n└── requirements.txt      # optional\n",[76,7216,7217,7222,7227,7235,7246,7258,7269,7281,7290,7299,7303,7313],{"__ignoreMap":187},[1238,7218,7219],{"class":1240,"line":1241},[1238,7220,7221],{"class":1497},"pytorch-onnx\u002F\n",[1238,7223,7224],{"class":1240,"line":191},[1238,7225,7226],{"class":1497},"│\n",[1238,7228,7229,7232],{"class":1240,"line":196},[1238,7230,7231],{"class":1497},"├──",[1238,7233,7234],{"class":1266}," data\u002F\n",[1238,7236,7237,7240,7243],{"class":1240,"line":188},[1238,7238,7239],{"class":1497},"│",[1238,7241,7242],{"class":1266},"   ├──",[1238,7244,7245],{"class":1266}," train\u002F\n",[1238,7247,7248,7250,7253,7255],{"class":1240,"line":1334},[1238,7249,7239],{"class":1497},[1238,7251,7252],{"class":1266},"   │",[1238,7254,7242],{"class":1266},[1238,7256,7257],{"class":1266}," apples\u002F\n",[1238,7259,7260,7262,7264,7266],{"class":1240,"line":1372},[1238,7261,7239],{"class":1497},[1238,7263,7252],{"class":1266},[1238,7265,7242],{"class":1266},[1238,7267,7268],{"class":1266}," kiwis\u002F\n",[1238,7270,7271,7273,7275,7278],{"class":1240,"line":1411},[1238,7272,7239],{"class":1497},[1238,7274,7252],{"class":1266},[1238,7276,7277],{"class":1266},"   └──",[1238,7279,7280],{"class":1266}," mangos\u002F\n",[1238,7282,7283,7285,7287],{"class":1240,"line":1807},[1238,7284,7239],{"class":1497},[1238,7286,7242],{"class":1266},[1238,7288,7289],{"class":1266}," val\u002F\n",[1238,7291,7292,7294,7296],{"class":1240,"line":1813},[1238,7293,7239],{"class":1497},[1238,7295,7277],{"class":1266},[1238,7297,7298],{"class":1266}," test\u002F\n",[1238,7300,7301],{"class":1240,"line":1819},[1238,7302,7226],{"class":1497},[1238,7304,7305,7307,7310],{"class":1240,"line":1825},[1238,7306,7231],{"class":1497},[1238,7308,7309],{"class":1266}," fruit_classifier.py",[1238,7311,7312],{"class":4041},"   # we will create this shortly\n",[1238,7314,7315,7318,7321],{"class":1240,"line":1831},[1238,7316,7317],{"class":1497},"└──",[1238,7319,7320],{"class":1266}," requirements.txt",[1238,7322,7323],{"class":4041},"      # optional\n",[143,7325,7327],{"id":7326},"fine-tune-the-model","Fine-tune the model",[15,7329,7330],{},"Now we can create a simple pytorch model to classify the images.",[15,7332,7333,7334],{},"Create a new file called ",[76,7335,7336],{},"fruit_classifier.py",[1195,7338,7340],{"className":1488,"code":7339,"language":1490,"meta":187,"style":187},"# Use nano to create the file (you can use your favorite editor e.g. vim, code, etc)\nnano fruit_classifier.py\n",[76,7341,7342,7347],{"__ignoreMap":187},[1238,7343,7344],{"class":1240,"line":1241},[1238,7345,7346],{"class":4041},"# Use nano to create the file (you can use your favorite editor e.g. vim, code, etc)\n",[1238,7348,7349,7352],{"class":1240,"line":191},[1238,7350,7351],{"class":1497},"nano",[1238,7353,7354],{"class":1266}," fruit_classifier.py\n",[15,7356,7357],{},"Add the following code:",[1195,7359,7361],{"className":1766,"code":7360,"language":1768,"meta":187,"style":187},"# fruit_classifier.py\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader\nimport torchvision.transforms as transforms\nimport torchvision.datasets as datasets\nimport torchvision.models as models\nimport onnxruntime as ort\nimport numpy as np\n\n# --- Dataset ---\ndata_dir = \"data\"\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\ntrain_dataset = datasets.ImageFolder(f\"{data_dir}\u002Ftrain\", transform=transform)\nval_dataset   = datasets.ImageFolder(f\"{data_dir}\u002Fval\", transform=transform)\ntest_dataset  = datasets.ImageFolder(f\"{data_dir}\u002Ftest\", transform=transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader   = DataLoader(val_dataset, batch_size=32, shuffle=False)\ntest_loader  = DataLoader(test_dataset, batch_size=32, shuffle=False)\n\nprint(\"Class mapping:\", train_dataset.class_to_idx)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# --- Model ---\nmodel = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)\nmodel.fc = nn.Linear(model.fc.in_features, len(train_dataset.classes))\nmodel = model.to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\n\n# --- Training ---\ndef train(num_epochs=5):\n    for epoch in range(num_epochs):\n        model.train()\n        running_loss = 0.0\n        for inputs, labels in train_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n\n            optimizer.zero_grad()\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            loss.backward()\n            optimizer.step()\n\n            running_loss += loss.item()\n\n        avg_loss = running_loss \u002F len(train_loader)\n        print(f\"Epoch {epoch+1}, Loss: {avg_loss:.4f}\")\n\n\n# --- Evaluation ---\ndef evaluate(loader):\n    model.eval()\n    correct, total = 0, 0\n    with torch.no_grad():\n        for inputs, labels in loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            _, preds = torch.max(outputs, 1)\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n    return correct \u002F total\n\n\n# --- Export to ONNX ---\ndef export_model():\n    dummy_input = torch.randn(1, 3, 224, 224, device=device)\n\n    torch.onnx.export(\n        model,               # model being run\n        dummy_input,         # model input (or a tuple for multiple inputs)\n        \"fruit_classifier.onnx\",    # where to save the model (can be a file or file-like object)\n        export_params=True,  # store the trained parameter weights inside the model file\n        opset_version=16,    # the ONNX version to export the model to\n        do_constant_folding=True,  # whether to execute constant folding for optimization\n        input_names=['input'],   # the model's input names\n        output_names=['output'],  # the model's output names\n        dynamic_axes={\"input\": {0: \"batch_size\"}, \"output\": {0: \"batch_size\"}}\n    )\n\n    print(\"Model exported to fruit_classifier.onnx\")\n\n\n# --- Test with ONNX Runtime ---\ndef test_onnx():\n    ort_session = ort.InferenceSession(\"fruit_classifier.onnx\")\n\n    def to_numpy(tensor):\n        return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()\n\n    inputs, _ = next(iter(test_loader))\n    ort_inputs = {\"input\": to_numpy(inputs[:1])}\n    ort_outs = ort_session.run(None, ort_inputs)\n\n    pred_class = np.argmax(ort_outs[0])\n    print(\"ONNX Prediction:\", train_dataset.classes[pred_class])\n\n\n# --- Main ---\nif __name__ == \"__main__\":\n    train(num_epochs=5)\n    val_acc = evaluate(val_loader)\n    print(f\"Validation Accuracy: {val_acc:.2%}\")\n\n    export_model()\n    test_onnx()\n\n",[76,7362,7363,7368,7372,7376,7380,7385,7390,7395,7400,7405,7409,7413,7417,7422,7427,7432,7437,7442,7447,7452,7457,7461,7466,7471,7476,7480,7485,7490,7495,7499,7504,7508,7513,7517,7522,7527,7532,7537,7541,7546,7551,7555,7559,7564,7569,7574,7579,7584,7589,7594,7598,7603,7608,7613,7618,7623,7627,7632,7636,7641,7646,7650,7654,7659,7664,7668,7673,7677,7682,7686,7690,7695,7700,7705,7710,7714,7718,7723,7728,7733,7737,7742,7747,7752,7757,7762,7767,7772,7777,7782,7787,7791,7795,7800,7804,7808,7813,7818,7823,7827,7832,7837,7841,7846,7851,7856,7860,7865,7870,7874,7878,7883,7888,7893,7898,7903,7907,7912],{"__ignoreMap":187},[1238,7364,7365],{"class":1240,"line":1241},[1238,7366,7367],{},"# fruit_classifier.py\n",[1238,7369,7370],{"class":1240,"line":191},[1238,7371,1789],{"emptyLinePlaceholder":216},[1238,7373,7374],{"class":1240,"line":196},[1238,7375,1816],{},[1238,7377,7378],{"class":1240,"line":188},[1238,7379,1822],{},[1238,7381,7382],{"class":1240,"line":1334},[1238,7383,7384],{},"import torch.optim as optim\n",[1238,7386,7387],{"class":1240,"line":1372},[1238,7388,7389],{},"from torch.utils.data import DataLoader\n",[1238,7391,7392],{"class":1240,"line":1411},[1238,7393,7394],{},"import torchvision.transforms as transforms\n",[1238,7396,7397],{"class":1240,"line":1807},[1238,7398,7399],{},"import torchvision.datasets as datasets\n",[1238,7401,7402],{"class":1240,"line":1813},[1238,7403,7404],{},"import torchvision.models as models\n",[1238,7406,7407],{"class":1240,"line":1819},[1238,7408,1834],{},[1238,7410,7411],{"class":1240,"line":1825},[1238,7412,1810],{},[1238,7414,7415],{"class":1240,"line":1831},[1238,7416,1789],{"emptyLinePlaceholder":216},[1238,7418,7419],{"class":1240,"line":1837},[1238,7420,7421],{},"# --- Dataset ---\n",[1238,7423,7424],{"class":1240,"line":1843},[1238,7425,7426],{},"data_dir = \"data\"\n",[1238,7428,7429],{"class":1240,"line":1849},[1238,7430,7431],{},"transform = transforms.Compose([\n",[1238,7433,7434],{"class":1240,"line":1855},[1238,7435,7436],{},"    transforms.Resize((224, 224)),\n",[1238,7438,7439],{"class":1240,"line":1860},[1238,7440,7441],{},"    transforms.ToTensor(),\n",[1238,7443,7444],{"class":1240,"line":1866},[1238,7445,7446],{},"    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n",[1238,7448,7449],{"class":1240,"line":1872},[1238,7450,7451],{},"                         std=[0.229, 0.224, 0.225])\n",[1238,7453,7454],{"class":1240,"line":1878},[1238,7455,7456],{},"])\n",[1238,7458,7459],{"class":1240,"line":1884},[1238,7460,1789],{"emptyLinePlaceholder":216},[1238,7462,7463],{"class":1240,"line":1890},[1238,7464,7465],{},"train_dataset = datasets.ImageFolder(f\"{data_dir}\u002Ftrain\", transform=transform)\n",[1238,7467,7468],{"class":1240,"line":1896},[1238,7469,7470],{},"val_dataset   = datasets.ImageFolder(f\"{data_dir}\u002Fval\", transform=transform)\n",[1238,7472,7473],{"class":1240,"line":1902},[1238,7474,7475],{},"test_dataset  = datasets.ImageFolder(f\"{data_dir}\u002Ftest\", transform=transform)\n",[1238,7477,7478],{"class":1240,"line":1907},[1238,7479,1789],{"emptyLinePlaceholder":216},[1238,7481,7482],{"class":1240,"line":1913},[1238,7483,7484],{},"train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n",[1238,7486,7487],{"class":1240,"line":1919},[1238,7488,7489],{},"val_loader   = DataLoader(val_dataset, batch_size=32, shuffle=False)\n",[1238,7491,7492],{"class":1240,"line":1925},[1238,7493,7494],{},"test_loader  = DataLoader(test_dataset, batch_size=32, shuffle=False)\n",[1238,7496,7497],{"class":1240,"line":1931},[1238,7498,1789],{"emptyLinePlaceholder":216},[1238,7500,7501],{"class":1240,"line":1937},[1238,7502,7503],{},"print(\"Class mapping:\", train_dataset.class_to_idx)\n",[1238,7505,7506],{"class":1240,"line":1943},[1238,7507,1789],{"emptyLinePlaceholder":216},[1238,7509,7510],{"class":1240,"line":1949},[1238,7511,7512],{},"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",[1238,7514,7515],{"class":1240,"line":1955},[1238,7516,1789],{"emptyLinePlaceholder":216},[1238,7518,7519],{"class":1240,"line":1960},[1238,7520,7521],{},"# --- Model ---\n",[1238,7523,7524],{"class":1240,"line":1966},[1238,7525,7526],{},"model = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)\n",[1238,7528,7529],{"class":1240,"line":1972},[1238,7530,7531],{},"model.fc = nn.Linear(model.fc.in_features, len(train_dataset.classes))\n",[1238,7533,7534],{"class":1240,"line":1977},[1238,7535,7536],{},"model = model.to(device)\n",[1238,7538,7539],{"class":1240,"line":1983},[1238,7540,1789],{"emptyLinePlaceholder":216},[1238,7542,7543],{"class":1240,"line":1989},[1238,7544,7545],{},"criterion = nn.CrossEntropyLoss()\n",[1238,7547,7548],{"class":1240,"line":1995},[1238,7549,7550],{},"optimizer = optim.Adam(model.parameters(), lr=1e-4)\n",[1238,7552,7553],{"class":1240,"line":2001},[1238,7554,1789],{"emptyLinePlaceholder":216},[1238,7556,7557],{"class":1240,"line":2007},[1238,7558,1789],{"emptyLinePlaceholder":216},[1238,7560,7561],{"class":1240,"line":2012},[1238,7562,7563],{},"# --- Training ---\n",[1238,7565,7566],{"class":1240,"line":2018},[1238,7567,7568],{},"def train(num_epochs=5):\n",[1238,7570,7571],{"class":1240,"line":2024},[1238,7572,7573],{},"    for epoch in range(num_epochs):\n",[1238,7575,7576],{"class":1240,"line":2030},[1238,7577,7578],{},"        model.train()\n",[1238,7580,7581],{"class":1240,"line":2036},[1238,7582,7583],{},"        running_loss = 0.0\n",[1238,7585,7586],{"class":1240,"line":2042},[1238,7587,7588],{},"        for inputs, labels in train_loader:\n",[1238,7590,7591],{"class":1240,"line":2048},[1238,7592,7593],{},"            inputs, labels = inputs.to(device), labels.to(device)\n",[1238,7595,7596],{"class":1240,"line":2054},[1238,7597,1789],{"emptyLinePlaceholder":216},[1238,7599,7600],{"class":1240,"line":2060},[1238,7601,7602],{},"            optimizer.zero_grad()\n",[1238,7604,7605],{"class":1240,"line":2066},[1238,7606,7607],{},"            outputs = model(inputs)\n",[1238,7609,7610],{"class":1240,"line":2072},[1238,7611,7612],{},"            loss = criterion(outputs, labels)\n",[1238,7614,7615],{"class":1240,"line":2078},[1238,7616,7617],{},"            loss.backward()\n",[1238,7619,7620],{"class":1240,"line":2084},[1238,7621,7622],{},"            optimizer.step()\n",[1238,7624,7625],{"class":1240,"line":2090},[1238,7626,1789],{"emptyLinePlaceholder":216},[1238,7628,7629],{"class":1240,"line":2095},[1238,7630,7631],{},"            running_loss += loss.item()\n",[1238,7633,7634],{"class":1240,"line":2101},[1238,7635,1789],{"emptyLinePlaceholder":216},[1238,7637,7638],{"class":1240,"line":2107},[1238,7639,7640],{},"        avg_loss = running_loss \u002F len(train_loader)\n",[1238,7642,7643],{"class":1240,"line":2113},[1238,7644,7645],{},"        print(f\"Epoch {epoch+1}, Loss: {avg_loss:.4f}\")\n",[1238,7647,7648],{"class":1240,"line":2118},[1238,7649,1789],{"emptyLinePlaceholder":216},[1238,7651,7652],{"class":1240,"line":2124},[1238,7653,1789],{"emptyLinePlaceholder":216},[1238,7655,7656],{"class":1240,"line":2130},[1238,7657,7658],{},"# --- Evaluation ---\n",[1238,7660,7661],{"class":1240,"line":2136},[1238,7662,7663],{},"def evaluate(loader):\n",[1238,7665,7666],{"class":1240,"line":2142},[1238,7667,2577],{},[1238,7669,7670],{"class":1240,"line":2148},[1238,7671,7672],{},"    correct, total = 0, 0\n",[1238,7674,7675],{"class":1240,"line":2153},[1238,7676,2583],{},[1238,7678,7679],{"class":1240,"line":2159},[1238,7680,7681],{},"        for inputs, labels in loader:\n",[1238,7683,7684],{"class":1240,"line":2165},[1238,7685,7593],{},[1238,7687,7688],{"class":1240,"line":2171},[1238,7689,7607],{},[1238,7691,7692],{"class":1240,"line":2177},[1238,7693,7694],{},"            _, preds = torch.max(outputs, 1)\n",[1238,7696,7697],{"class":1240,"line":2183},[1238,7698,7699],{},"            correct += (preds == labels).sum().item()\n",[1238,7701,7702],{"class":1240,"line":2189},[1238,7703,7704],{},"            total += labels.size(0)\n",[1238,7706,7707],{"class":1240,"line":2195},[1238,7708,7709],{},"    return correct \u002F total\n",[1238,7711,7712],{"class":1240,"line":2201},[1238,7713,1789],{"emptyLinePlaceholder":216},[1238,7715,7716],{"class":1240,"line":2206},[1238,7717,1789],{"emptyLinePlaceholder":216},[1238,7719,7720],{"class":1240,"line":2212},[1238,7721,7722],{},"# --- Export to ONNX ---\n",[1238,7724,7725],{"class":1240,"line":2218},[1238,7726,7727],{},"def export_model():\n",[1238,7729,7730],{"class":1240,"line":2224},[1238,7731,7732],{},"    dummy_input = torch.randn(1, 3, 224, 224, device=device)\n",[1238,7734,7735],{"class":1240,"line":2230},[1238,7736,1789],{"emptyLinePlaceholder":216},[1238,7738,7739],{"class":1240,"line":2236},[1238,7740,7741],{},"    torch.onnx.export(\n",[1238,7743,7744],{"class":1240,"line":2242},[1238,7745,7746],{},"        model,               # model being run\n",[1238,7748,7749],{"class":1240,"line":2248},[1238,7750,7751],{},"        dummy_input,         # model input (or a tuple for multiple inputs)\n",[1238,7753,7754],{"class":1240,"line":2254},[1238,7755,7756],{},"        \"fruit_classifier.onnx\",    # where to save the model (can be a file or file-like object)\n",[1238,7758,7759],{"class":1240,"line":2260},[1238,7760,7761],{},"        export_params=True,  # store the trained parameter weights inside the model file\n",[1238,7763,7764],{"class":1240,"line":2266},[1238,7765,7766],{},"        opset_version=16,    # the ONNX version to export the model to\n",[1238,7768,7769],{"class":1240,"line":2271},[1238,7770,7771],{},"        do_constant_folding=True,  # whether to execute constant folding for optimization\n",[1238,7773,7774],{"class":1240,"line":2277},[1238,7775,7776],{},"        input_names=['input'],   # the model's input names\n",[1238,7778,7779],{"class":1240,"line":2283},[1238,7780,7781],{},"        output_names=['output'],  # the model's output names\n",[1238,7783,7784],{"class":1240,"line":2289},[1238,7785,7786],{},"        dynamic_axes={\"input\": {0: \"batch_size\"}, \"output\": {0: \"batch_size\"}}\n",[1238,7788,7789],{"class":1240,"line":2295},[1238,7790,2801],{},[1238,7792,7793],{"class":1240,"line":2300},[1238,7794,1789],{"emptyLinePlaceholder":216},[1238,7796,7797],{"class":1240,"line":2306},[1238,7798,7799],{},"    print(\"Model exported to fruit_classifier.onnx\")\n",[1238,7801,7802],{"class":1240,"line":2312},[1238,7803,1789],{"emptyLinePlaceholder":216},[1238,7805,7806],{"class":1240,"line":2318},[1238,7807,1789],{"emptyLinePlaceholder":216},[1238,7809,7810],{"class":1240,"line":2323},[1238,7811,7812],{},"# --- Test with ONNX Runtime ---\n",[1238,7814,7815],{"class":1240,"line":2329},[1238,7816,7817],{},"def test_onnx():\n",[1238,7819,7820],{"class":1240,"line":2335},[1238,7821,7822],{},"    ort_session = ort.InferenceSession(\"fruit_classifier.onnx\")\n",[1238,7824,7825],{"class":1240,"line":2341},[1238,7826,1789],{"emptyLinePlaceholder":216},[1238,7828,7829],{"class":1240,"line":2347},[1238,7830,7831],{},"    def to_numpy(tensor):\n",[1238,7833,7834],{"class":1240,"line":2352},[1238,7835,7836],{},"        return tensor.detach().cpu().numpy() if tensor.requires_grad else tensor.cpu().numpy()\n",[1238,7838,7839],{"class":1240,"line":2358},[1238,7840,1789],{"emptyLinePlaceholder":216},[1238,7842,7843],{"class":1240,"line":2364},[1238,7844,7845],{},"    inputs, _ = next(iter(test_loader))\n",[1238,7847,7848],{"class":1240,"line":2369},[1238,7849,7850],{},"    ort_inputs = {\"input\": to_numpy(inputs[:1])}\n",[1238,7852,7853],{"class":1240,"line":2375},[1238,7854,7855],{},"    ort_outs = ort_session.run(None, ort_inputs)\n",[1238,7857,7858],{"class":1240,"line":2381},[1238,7859,1789],{"emptyLinePlaceholder":216},[1238,7861,7862],{"class":1240,"line":2387},[1238,7863,7864],{},"    pred_class = np.argmax(ort_outs[0])\n",[1238,7866,7867],{"class":1240,"line":2393},[1238,7868,7869],{},"    print(\"ONNX Prediction:\", train_dataset.classes[pred_class])\n",[1238,7871,7872],{"class":1240,"line":2398},[1238,7873,1789],{"emptyLinePlaceholder":216},[1238,7875,7876],{"class":1240,"line":2404},[1238,7877,1789],{"emptyLinePlaceholder":216},[1238,7879,7880],{"class":1240,"line":2410},[1238,7881,7882],{},"# --- Main ---\n",[1238,7884,7885],{"class":1240,"line":2416},[1238,7886,7887],{},"if __name__ == \"__main__\":\n",[1238,7889,7890],{"class":1240,"line":2422},[1238,7891,7892],{},"    train(num_epochs=5)\n",[1238,7894,7895],{"class":1240,"line":2428},[1238,7896,7897],{},"    val_acc = evaluate(val_loader)\n",[1238,7899,7900],{"class":1240,"line":2434},[1238,7901,7902],{},"    print(f\"Validation Accuracy: {val_acc:.2%}\")\n",[1238,7904,7905],{"class":1240,"line":2440},[1238,7906,1789],{"emptyLinePlaceholder":216},[1238,7908,7909],{"class":1240,"line":2446},[1238,7910,7911],{},"    export_model()\n",[1238,7913,7914],{"class":1240,"line":2452},[1238,7915,7916],{},"    test_onnx()\n",[6694,7918,7920],{"id":7919},"run-the-fruit_classifierpy-python-script","Run the fruit_classifier.py Python script",[15,7922,7923],{},"Now you can run the script that will train the model, export it to ONNX format, and run a quick classification test using the ONNX Runtime:",[1195,7925,7927],{"className":1488,"code":7926,"language":1490,"meta":187,"style":187},"python fruit_classifier.py\n",[76,7928,7929],{"__ignoreMap":187},[1238,7930,7931,7933],{"class":1240,"line":1241},[1238,7932,1768],{"class":1497},[1238,7934,7354],{"class":1266},[15,7936,7937],{},"What you should see:",[1195,7939,7943],{"className":7940,"code":7941,"language":7942,"meta":187,"style":187},"language-log shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","Class mapping: {'apple': 0, 'kiwi': 1, 'mango': 2}\nEpoch 1, Loss: 0.7811\nEpoch 2, Loss: 0.1383\nEpoch 3, Loss: 0.0671\nEpoch 4, Loss: 0.0399\nEpoch 5, Loss: 0.0184\nValidation Accuracy: 80.95%\nModel exported to fruit_classifier.onnx\nONNX Prediction: apple\n","log",[76,7944,7945,7950,7955,7960,7965,7970,7975,7980,7985],{"__ignoreMap":187},[1238,7946,7947],{"class":1240,"line":1241},[1238,7948,7949],{},"Class mapping: {'apple': 0, 'kiwi': 1, 'mango': 2}\n",[1238,7951,7952],{"class":1240,"line":191},[1238,7953,7954],{},"Epoch 1, Loss: 0.7811\n",[1238,7956,7957],{"class":1240,"line":196},[1238,7958,7959],{},"Epoch 2, Loss: 0.1383\n",[1238,7961,7962],{"class":1240,"line":188},[1238,7963,7964],{},"Epoch 3, Loss: 0.0671\n",[1238,7966,7967],{"class":1240,"line":1334},[1238,7968,7969],{},"Epoch 4, Loss: 0.0399\n",[1238,7971,7972],{"class":1240,"line":1372},[1238,7973,7974],{},"Epoch 5, Loss: 0.0184\n",[1238,7976,7977],{"class":1240,"line":1411},[1238,7978,7979],{},"Validation Accuracy: 80.95%\n",[1238,7981,7982],{"class":1240,"line":1807},[1238,7983,7984],{},"Model exported to fruit_classifier.onnx\n",[1238,7986,7987],{"class":1240,"line":1813},[1238,7988,7989],{},"ONNX Prediction: apple\n",[15,7991,7992,7993,7996],{},"If you changed the data set from fruit to your use own images and classifications, it will output a different ",[76,7994,7995],{},"Class mapping"," that you will need to use in the Node-RED flow on the next step - make a note of this.",[143,7998,8000],{"id":7999},"using-your-newly-generated-onnx-model-with-the-flowfuse-onnx-node","Using your newly generated ONNX Model with the FlowFuse ONNX Node",[3084,8002,8003,8011,8055,8058,8065,8072,8075],{},[50,8004,8005,8006],{},"Import the finished ONNX model into a location in the file system where your Node-RED instance can access it\n",[47,8007,8008],{},[50,8009,8010],{},"In FlowFuse cloud you can do this via the Assets tab",[50,8012,8013,8014],{},"Import the demo flow\n",[47,8015,8016,8019,8030,8037,8044,8050],{},[50,8017,8018],{},"Open your Node-RED editor",[50,8020,8021,8022,8025,8026,8029],{},"Press ",[76,8023,8024],{},"CTRL-I"," or select ",[76,8027,8028],{},"Import"," from the menu to open the Import Dialog",[50,8031,8032,8033,8036],{},"Select the ",[53,8034,8035],{},"Examples"," tab",[50,8038,8039,8040,8043],{},"Click the ",[53,8041,8042],{},"@FlowFuse\u002Fnr-ai-nodes"," entry",[50,8045,8046,8047],{},"Click the demo named ",[53,8048,8049],{},"advanced-custom-model",[50,8051,8039,8052,8054],{},[53,8053,8028],{}," Button",[50,8056,8057],{},"Double click the ONNX node to open the configuration dialog",[50,8059,8060,8061,8064],{},"Enter the path to your ONNX model in the ",[53,8062,8063],{},"Path"," field",[50,8066,8067,8068,8071],{},"If necessary, update the classifications (labels) in the Function node named ",[53,8069,8070],{},"load labels"," as noted in the previous section",[50,8073,8074],{},"Deploy the flow",[50,8076,8077],{},"Click the inject button on the left of the flow to trigger an inference",[15,8079,8080,8084],{},[30,8081],{"alt":8082,"dataZoomable":187,"src":8083},"Image showing how to import demo flow","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fcustom-onnx-mode--import-flow.png",[35,8085,8082],{},[15,8087,8088,8092],{},[30,8089],{"alt":8090,"dataZoomable":187,"src":8091},"Image showing inference in action","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fcustom-onnx-mode--in-action.png",[35,8093,8090],{},[143,8095,8097],{"id":8096},"supplementary-notes","Supplementary Notes",[6694,8099,8101],{"id":8100},"clean-up","Clean up",[15,8103,8104],{},"To deactivate the virtual environment when you're done, simply run:",[1195,8106,8108],{"className":1488,"code":8107,"language":1490,"meta":187,"style":187},"pyenv deactivate\n",[76,8109,8110],{"__ignoreMap":187},[1238,8111,8112,8114],{"class":1240,"line":1241},[1238,8113,6703],{"class":1497},[1238,8115,8116],{"class":1266}," deactivate\n",[15,8118,8119,8120,8123],{},"You can remove the ",[76,8121,8122],{},"__pycache__"," and other temporary files if they were created:",[1195,8125,8127],{"className":1488,"code":8126,"language":1490,"meta":187,"style":187},"rm -rf __pycache__\nrm -rf runs\u002F logs\u002F checkpoints\u002F\n",[76,8128,8129,8140],{"__ignoreMap":187},[1238,8130,8131,8134,8137],{"class":1240,"line":1241},[1238,8132,8133],{"class":1497},"rm",[1238,8135,8136],{"class":1266}," -rf",[1238,8138,8139],{"class":1266}," __pycache__\n",[1238,8141,8142,8144,8146,8149,8152],{"class":1240,"line":191},[1238,8143,8133],{"class":1497},[1238,8145,8136],{"class":1266},[1238,8147,8148],{"class":1266}," runs\u002F",[1238,8150,8151],{"class":1266}," logs\u002F",[1238,8153,8154],{"class":1266}," checkpoints\u002F\n",[15,8156,8157],{},"If you want to completely remove the virtual environment, you can do so with:",[1195,8159,8161],{"className":1488,"code":8160,"language":1490,"meta":187,"style":187},"pyenv uninstall venv_py3_10_14_pytorch\n",[76,8162,8163],{"__ignoreMap":187},[1238,8164,8165,8167,8170],{"class":1240,"line":1241},[1238,8166,6703],{"class":1497},[1238,8168,8169],{"class":1266}," uninstall",[1238,8171,6857],{"class":1266},[5509,8173,8174],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}",{"title":187,"searchDepth":188,"depth":188,"links":8176},[8177,8178,8179,8180,8187,8188,8191,8192],{"id":6648,"depth":196,"text":6649},{"id":6663,"depth":196,"text":6664},{"id":6670,"depth":196,"text":6671},{"id":6691,"depth":196,"text":6692,"children":8181},[8182,8186],{"id":6696,"depth":188,"text":6697,"children":8183},[8184,8185],{"id":6714,"depth":1334,"text":6715},{"id":6757,"depth":1334,"text":6758},{"id":6819,"depth":188,"text":6820},{"id":6999,"depth":196,"text":7000},{"id":7326,"depth":196,"text":7327,"children":8189},[8190],{"id":7919,"depth":188,"text":7920},{"id":7999,"depth":196,"text":8000},{"id":8096,"depth":196,"text":8097,"children":8193},[8194],{"id":8100,"depth":188,"text":8101},"2025-10-10","Learn how to train and export an image classifier model, and integrate it with FlowFuse AI Nodes for low-code inference in Node-RED.","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fflowfuse-ai-nodes.png",{"keywords":8199,"excerpt":8200},"FlowFuse, Node-RED, industrial automation, low-code platform, data analysis, vision systems, inference, AI, object detection, image classification, depth estimation, transfer learning, PyTorch, ONNX, ResNet",{"type":12,"value":8201},[8202],[15,8203,6645],{},"\u002Fblog\u002F2025\u002F10\u002Fcustom-onnx-model",{"title":6638,"description":8196},{"loc":8204},"blog\u002F2025\u002F10\u002Fcustom-onnx-model","Using ONNX runtime to run inference in Node-RED",[223,224],"qXMpnqtaoUM3jiwmhm4Ix8_KlQghgfftJlPrg7Zcxxk",{"id":8212,"title":8213,"authors":8214,"body":8216,"cta":3,"date":8307,"description":8308,"extension":207,"image":8309,"lastUpdated":3,"meta":8310,"navigation":216,"path":8320,"seo":8321,"sitemap":8322,"stem":8323,"subtitle":8324,"tags":8325,"tldr":3,"video":3,"__hash__":8326},"blog\u002Fblog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype.md","Beyond Cloud AI Orchestration: Why the Future is Hybrid Edge-Cloud Intelligence",[8215],"pablo-filomeno",{"type":12,"value":8217,"toc":8300},[8218,8227,8230,8233,8236,8240,8243,8246,8249,8253,8256,8259,8263,8266,8269,8272,8276,8279,8282,8285,8289,8292],[15,8219,8220,8221,8226],{},"Congratulations to n8n on their ",[22,8222,8225],{"href":8223,"rel":8224},"https:\u002F\u002Fblog.n8n.io\u002Fseries-c\u002F",[445],"Series C funding round","! This is a fantastic milestone and a clear signal that the market has moved beyond AI experimentation and into the serious business of production deployment. Platforms like n8n are mastering what we call centralized orchestration: creating cloud-native \"brains\" that connect digital services, APIs, and data sources to execute complex workflows. This approach excels for digital-first applications and represents a crucial evolution in workflow automation.",[15,8228,8229],{},"But for industrial applications, we need to think beyond traditional cloud orchestration.",[15,8231,8232],{},"The tech world is buzzing with talk of AI agents and orchestration platforms, and the energy around n8n's recent funding is proof of this momentum. This energy is a fantastic sign of a maturing market, proving that we've moved beyond AI experimentation and into the serious business of production deployment.",[15,8234,8235],{},"Cloud orchestration platforms excel at what they're designed for: connecting digital services, managing API workflows, and orchestrating cloud-native applications. However, for the industries that power our physical world, manufacturing, logistics, energy, and infrastructure, we need complementary approaches that address the unique requirements of operational technology. This is where hybrid edge-cloud architectures become essential.",[39,8237,8239],{"id":8238},"industrial-requirements-where-cloud-only-solutions-face-challenges","Industrial Requirements: Where Cloud-Only Solutions Face Challenges",[15,8241,8242],{},"When operations involve real-world assets like factory machinery, remote sensors, or logistics fleets, purely centralized approaches encounter specific industrial constraints. For applications where milliseconds matter, such as emergency shutdowns or quality control decisions, the latency of cloud round-trips can be too slow for critical responses. Connectivity challenges in industrial environments, from remote oil platforms to underground mining operations, require systems that can operate intelligently even when cloud connections are intermittent.",[15,8244,8245],{},"Additionally, the economics of data movement become significant at industrial scale. Streaming continuous data from thousands of sensors to the cloud for processing can be costly and inefficient, especially when much of that processing could happen locally. Many industries also have regulatory requirements that mandate certain operational data remain on-premise for compliance and security reasons.",[15,8247,8248],{},"These constraints don't invalidate cloud orchestration, they highlight the need for hybrid approaches that leverage both cloud capabilities and edge intelligence where each excels.",[39,8250,8252],{"id":8251},"the-next-paradigm-hybrid-edge-cloud-intelligence","The Next Paradigm: Hybrid Edge-Cloud Intelligence",[15,8254,8255],{},"The future of industrial AI isn't choosing between cloud or edge, it's intelligently combining both. Rather than replacing cloud orchestration, the industrial world needs a hybrid architecture: a distributed nervous system where cloud orchestration handles high-level coordination and data aggregation, while edge intelligence manages real-time operations and local decision-making.",[15,8257,8258],{},"In this paradigm, AI operates at multiple levels. Edge devices handle immediate responses, predictive maintenance alerts, quality control decisions, and safety shutdowns, without waiting for cloud communication. Meanwhile, cloud orchestration excels at what it does best: aggregating data from multiple sites, running complex analytics, coordinating across systems, and managing enterprise-wide workflows. This creates a complementary relationship where each layer operates within its strengths.",[39,8260,8262],{"id":8261},"flowfuse-bridging-edge-and-cloud-intelligence","FlowFuse: Bridging Edge and Cloud Intelligence",[15,8264,8265],{},"While the market builds excellent tools for cloud-based AI orchestration, FlowFuse specializes in the hybrid approach that industrial applications demand. Built on the proven foundation of Node-RED, our platform provides engineers with a unified control tower to manage both cloud orchestration and edge intelligence at scale.",[15,8267,8268],{},"FlowFuse Cloud enables centralized management and coordination, while our edge capabilities allow thousands of Node-RED instances to operate intelligently at remote locations. This hybrid architecture lets you seamlessly integrate the physical and digital worlds, leveraging cloud orchestration for enterprise workflows while maintaining real-time edge intelligence where it matters most.",[15,8270,8271],{},"Our FlowFuse Expert simplifies the creation and management of complex logic across both cloud and edge environments, democratizing advanced automation without requiring specialized data science skills. Soon, with our upcoming AI Agent nodes, FlowFuse will enable powerful AI agents to operate seamlessly across the entire hybrid architecture, from cloud coordination to edge execution.",[39,8273,8275],{"id":8274},"conclusion-the-power-of-hybrid-intelligence","Conclusion: The Power of Hybrid Intelligence",[15,8277,8278],{},"The rise of AI orchestration platforms represents an important evolution in automation technology. These tools excel in their domain and will continue to play a crucial role in digital transformation initiatives.",[15,8280,8281],{},"For industrial applications, the future lies in hybrid architectures that combine the strengths of both approaches. Cloud orchestration provides the coordination, analytics, and enterprise integration capabilities that modern businesses require, while edge intelligence delivers the real-time responsiveness and resilience that industrial operations demand.",[15,8283,8284],{},"Choosing the right approach comes down to understanding your requirements. For digital-first applications, cloud orchestration platforms offer powerful solutions. For industrial and IoT applications that bridge physical and digital worlds, hybrid edge-cloud architectures provide the comprehensive intelligence needed to succeed.",[39,8286,8288],{"id":8287},"ready-to-build-the-future-of-industrial-ai","Ready to Build the Future of Industrial AI?",[15,8290,8291],{},"FlowFuse is built for the challenges of industrial and IoT data. Whether you're implementing Industry 4.0 initiatives or building predictive maintenance systems, we provide the platform to deploy and manage intelligence across both cloud and edge environments.",[15,8293,8294,8295],{},"[Start building today: Try FlowFuse free]({% include \"sign-up-url.njk\" %}) or ",[22,8296,8299],{"href":8297,"rel":8298},"https:\u002F\u002Fflowfuse.com\u002Fbook-demo\u002F",[445],"Book a demo",{"title":187,"searchDepth":188,"depth":188,"links":8301},[8302,8303,8304,8305,8306],{"id":8238,"depth":191,"text":8239},{"id":8251,"depth":191,"text":8252},{"id":8261,"depth":191,"text":8262},{"id":8274,"depth":191,"text":8275},{"id":8287,"depth":191,"text":8288},"2025-10-09","How edge-cloud hybrid AI architectures unlock new possibilities for industrial applications while leveraging the best of both worlds.","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fthe-ai-orchestration-hype.png",{"keywords":3,"excerpt":8311},{"type":12,"value":8312},[8313,8318],[15,8314,8220,8315,8226],{},[22,8316,8225],{"href":8223,"rel":8317},[445],[15,8319,8229],{},"\u002Fblog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype",{"title":8213,"description":8308},{"loc":8320},"blog\u002F2025\u002F10\u002Fthe-ai-orchestration-hype","While cloud orchestration excels in digital workflows, the industrial world needs a hybrid approach.",[223,437,224],"lTP9N_yKxFosx6FdXJwfkCrat_ktdgrLHTdszCT_sY4",{"id":8328,"title":8329,"authors":8330,"body":8331,"cta":3,"date":8426,"description":8427,"extension":207,"image":8428,"lastUpdated":3,"meta":8429,"navigation":216,"path":8435,"seo":8436,"sitemap":8437,"stem":8438,"subtitle":8439,"tags":8440,"tldr":3,"video":3,"__hash__":8442},"blog\u002Fblog\u002F2025\u002F10\u002Fopen-ai-agent-builder-versus-flowfuse.md","OpenAI's AgentKit or FlowFuse: Choosing the Right Low-Code App for Your Needs",[538],{"type":12,"value":8332,"toc":8419},[8333,8336,8340,8348,8352,8358,8361,8365,8368,8371,8377,8380,8388,8392,8395,8398,8402,8405,8408],[15,8334,8335],{},"AI is moving fast, and with it, the tools we use to build intelligent applications.\nTwo interesting platforms that have emerged are OpenAI's AgentKit and FlowFuse.\nWhile both offer AI capabilities, they are designed for very different purposes.\nLet's break down the key differences to help you decide which platform is the\nright fit for your needs.",[39,8337,8339],{"id":8338},"openais-agentkit-for-building-ai-agents","OpenAI's AgentKit: For building AI agents",[15,8341,8342,8347],{},[22,8343,8346],{"href":8344,"rel":8345},"https:\u002F\u002Fopenai.com\u002Findex\u002Fintroducing-agentkit\u002F",[445],"OpenAI's AgentKit"," is a toolkit for developers who want to build and deploy AI\nagents. Key features of AgentKit include the Agent Builder, a low-code visual environment\nfor designing multi-agent workflows, and a Connector Registry\nfor managing data and tool connections.\nIt also provides ChatKit for embedding chat-based agent experiences,\nExpanded Evals for measuring agent performance, and reinforcement fine-tuning (RFT)\nto customize reasoning models. Essentially, AgentKit is for developers who are\nbuilding AI-native applications and need a robust set of tools to create and\nmanage their agents within the OpenAI ecosystem.",[39,8349,8351],{"id":8350},"flowfuse-the-bridge-between-the-physical-and-digital-worlds","FlowFuse: The Bridge Between the Physical and Digital Worlds",[15,8353,8354,8355,8357],{},"FlowFuse, on the other hand, is built on the foundation of ",[22,8356,438],{"href":1043},",\nalso a low-code platform. FlowFuse is specifically designed for industrial and\nIoT applications, with a strong focus on what's known as \"the edge\" – the\nphysical world where data is generated by assets like sensors, machines, and other\ndevices.",[15,8359,8360],{},"This is where the user's point about edge data extraction comes in. FlowFuse\nexcels at managing and scaling fleets of Node-RED instances running on edge\ndevices. This allows engineers to easily and securely collect data from all their\nindustrial devices and sensors, effectively \"fusing the physical with the digital.\"",[39,8362,8364],{"id":8363},"key-differentiators","Key Differentiators",[15,8366,8367],{},"So, how do these two platforms really differ? The first key differentiator is\ntheir core focus. AgentKit is for building AI agents that live in the digital\nworld of applications and services, whereas FlowFuse is for building and managing\nend-to-end data applications that interact with the physical world through edge\ndevices.",[15,8369,8370],{},"AgentKit, in its current form, does not have a focus on edge data extraction.\nIts purpose is to help you build the \"brains\" of an AI. FlowFuse's entire reason\nfor being is to provide the \"nervous system\" that connects those brains to the\nreal world. It's all about managing edge deployments and ensuring a reliable flow\nof data from the edge.",[15,8372,1420,8373,8376],{},[22,8374,714],{"href":8375},"\u002Fblog\u002F2025\u002F07\u002Fflowfuse-ai-assistant-better-node-red-manufacturing\u002F"," is a powerful tool that helps engineers, even those\nwho aren't expert coders, to build and manage their Node-RED flows. For instance,\nyou can describe what you need in plain English and the assistant will generate\nthe necessary code. It can also analyze a complex flow and explain what it does,\nmaking it easier to maintain. Furthermore, the assistant is a huge time-saver as\nit can create realistic test data and even help you build custom dashboards to\nvisualize your data.",[15,8378,8379],{},"So, while AgentKit is a toolkit for building AI agents, the FlowFuse Expert\nis a tool that helps you build the applications that connect to the physical world.\nIt empowers any engineer to fuse the physical with the digital by making it easier\nthan ever to create the logic needed to collect, transform, and act on data from\nthe edge.",[15,8381,8382,8383,8387],{},"What's more, FlowFuse already supports creating ",[22,8384,8386],{"href":8385},"\u002Fblog\u002F2025\u002F07\u002Fflowfuse-release-2-20\u002F#new-blueprint%3A-agentic-ai-with-retrieval-augmented-generation","AI and RAG"," integrations and will soon be releasing dedicated MCP nodes that greatly simplifies low-code building of AI Agents.\nenabling AI to work directly at the edge. This means you can create AI\nagents that not only process data from physical devices but also make decisions\nright where the data is generated, with a lot less latency due to less Cloud\nround-trips. This is especially important for industrial applications where\nmilliseconds matter.",[39,8389,8391],{"id":8390},"conclusion","Conclusion",[15,8393,8394],{},"Choosing between OpenAI's AgentKit and FlowFuse comes down to what you're trying\nto achieve. If your goal is to build sophisticated, AI-powered agents for your\napplications and your focus is on the digital realm, then OpenAI's AgentKit is\nthe clear choice. However, if you need to connect to, manage, and extract data\nfrom physical devices in the real world, and you want to empower your engineers\nwith an FlowFuse Expert that makes this process easier, then FlowFuse is the\nplatform for you.",[15,8396,8397],{},"In the end, these are two powerful but very different tools. AgentKit is for the\nAI developer, while FlowFuse is for the industrial engineer who wants to bring\nthe power of AI to the edge. We're looking forward to how these technologies can\nbe combined in future!",[39,8399,8401],{"id":8400},"ready-to-connect-your-physical-world","Ready to Connect Your Physical World?",[15,8403,8404],{},"While AgentKit is perfect for building AI agents in the digital realm, FlowFuse\nis built for the challenges of industrial and IoT data: managing edge\ndeployments at scale, connecting to diverse industrial protocols, and ensuring\nreliable data flow from thousands of physical devices.",[15,8406,8407],{},"Whether you're monitoring production lines, building predictive maintenance\nsystems, or implementing Industry 4.0 initiatives, FlowFuse provides the\ninfrastructure to collect, transform, and act on data from the edge.",[15,8409,8410,8413,8414,8418],{},[53,8411,8412],{},"Start building today:"," [Try FlowFuse free]({% include \"sign-up-url.njk\" %}) or  ",[22,8415,8417],{"href":8297,"rel":8416},[445],"book a demo"," to\nsee how we help teams manage industrial data at scale.",{"title":187,"searchDepth":188,"depth":188,"links":8420},[8421,8422,8423,8424,8425],{"id":8338,"depth":191,"text":8339},{"id":8350,"depth":191,"text":8351},{"id":8363,"depth":191,"text":8364},{"id":8390,"depth":191,"text":8391},{"id":8400,"depth":191,"text":8401},"2025-10-07","Learn how OpenAI's AgentKit and FlowFuse differ in their approach to AI agents, and discover which platform is right for building applications that connect the physical and digital worlds.","\u002Fblog\u002F2025\u002F10\u002Fimages\u002Fopen-ai-agent-builder-versus-flowfuse.png",{"keywords":8430,"excerpt":8431},"OpenAI AgentKit, FlowFuse, AI agents, Node-RED, edge computing, IoT, industrial automation, AI Assistant, low-code platform, edge data extraction",{"type":12,"value":8432},[8433],[15,8434,8335],{},"\u002Fblog\u002F2025\u002F10\u002Fopen-ai-agent-builder-versus-flowfuse",{"title":8329,"description":8427},{"loc":8435},"blog\u002F2025\u002F10\u002Fopen-ai-agent-builder-versus-flowfuse","Understanding the key differences between AI-native agent development and edge-focused industrial automation",[223,8441,224],"posts","ncT8y9jmgsJAwciAiWdqZczZuM4jFZl2QU4kFDxAvro",{"id":8444,"title":8445,"authors":8446,"body":8447,"cta":3,"date":8705,"description":8706,"extension":207,"image":8707,"lastUpdated":3,"meta":8708,"navigation":216,"path":8714,"seo":8715,"sitemap":8716,"stem":8717,"subtitle":8718,"tags":8719,"tldr":3,"video":3,"__hash__":8720},"blog\u002Fblog\u002F2025\u002F09\u002Fai-assistant-flowfuse-tables.md","Query Your Database with Natural Language Using FlowFuse Expert",[10],{"type":12,"value":8448,"toc":8695},[8449,8452,8456,8459,8462,8465,8473,8477,8480,8493,8505,8549,8552,8559,8562,8588,8599,8603,8606,8610,8618,8624,8632,8635,8639,8647,8650,8654,8657,8665,8668,8676,8679,8682,8686,8689,8692],[15,8450,8451],{},"Getting data from your database used to mean writing SQL queries. Not anymore. The FlowFuse Expert now lets you ask for what you want in plain English and automatically generates the SQL for you in query node.",[39,8453,8455],{"id":8454},"removing-technical-barriers","Removing Technical Barriers",[15,8457,8458],{},"Industrial operations generate massive amounts of valuable data from sensors, equipment, and PLCs. This data can drive optimization and cost savings, but extracting insights often requires SQL skills that not every team member possesses.",[15,8460,8461],{},"FlowFuse already makes it simple to connect to databases and build data flows using its Query nodes. However, the need to manually write SQL queries has remained a significant barrier for many users.",[15,8463,8464],{},"To address this, FlowFuse continues its mission of making industrial automation accessible to everyone, regardless of coding expertise. Features like the FlowFuse Expert have already reduced complexity by enabling users to create custom functions and UI components using natural language.",[15,8466,8467,8468,8472],{},"With FlowFuse ",[22,8469,8471],{"href":8470},"\u002Fblog\u002F2025\u002F08\u002Fflowfuse-release-2-21\u002F","2.21",", this ease of use extends to database queries as well. Users can now ask questions in plain English and have SQL automatically generated, removing the last major hurdle and empowering a broader audience to gain actionable insights quickly and easily.",[39,8474,8476],{"id":8475},"getting-started","Getting Started",[15,8478,8479],{},"Let's see how this works with a practical example. This feature combines two FlowFuse components:",[47,8481,8482,8488],{},[50,8483,8484,8487],{},[53,8485,8486],{},"FlowFuse Tables"," provides the database connectivity and Query nodes",[50,8489,8490,8492],{},[53,8491,714],{}," adds the natural language processing capability that converts plain English into SQL",[15,8494,8495,8496,8500,8501,8504],{},"Before you begin, make sure FlowFuse Tables is activated in your FlowFuse team. For more information, refer to ",[22,8497,8499],{"href":8498},"\u002Fblog\u002F2025\u002F08\u002Fgetting-started-with-flowfuse-tables\u002F","Getting Started with FlowFuse Tables",". Then, import the following flow and deploy it to create a ",[76,8502,8503],{},"sensor_readings"," table for practice:",[15,8506,8507,8508,8548],{},"{% renderFlow 300 %}\n",[1238,8509,8510,8511,8514,8515,8517,8518,8520,8521,8524,8525,8527,8528,8531,8532,8535,8536,8538,8539,8541,8542,8544,8545,8547],{},"{\"id\":\"e9a3b71d40addd8b\",\"type\":\"group\",\"z\":\"d74afda3e83a644e\",\"name\":\"Create Table\",\"style\":{\"label\":true},\"nodes\":",[1238,8512,8513],{},"\"48b1380ff7bfe716\",\"44bb8750d961e415\",\"4a3976e062b2ddd2\"",",\"x\":274,\"y\":419,\"w\":572,\"h\":82},{\"id\":\"48b1380ff7bfe716\",\"type\":\"inject\",\"z\":\"d74afda3e83a644e\",\"g\":\"e9a3b71d40addd8b\",\"name\":\"\",\"props\":",[1238,8516],{},",\"repeat\":\"\",\"crontab\":\"\",\"once\":true,\"onceDelay\":\"1\",\"topic\":\"\",\"x\":370,\"y\":460,\"wires\":[[\"44bb8750d961e415\"]]},{\"id\":\"44bb8750d961e415\",\"type\":\"tables-query\",\"z\":\"d74afda3e83a644e\",\"g\":\"e9a3b71d40addd8b\",\"name\":\"Create Table\",\"query\":\"CREATE TABLE IF NOT EXISTS public.sensor_readings ( (\\n    id SERIAL PRIMARY KEY,\\n    timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),\\n    sensor_id VARCHAR(50) NOT NULL,\\n    location VARCHAR(100),\\n    temperature DECIMAL(5,2)\\n);\\n\",\"split\":false,\"rowsPerMsg\":1,\"x\":550,\"y\":460,\"wires\":[[\"4a3976e062b2ddd2\"]]},{\"id\":\"4a3976e062b2ddd2\",\"type\":\"debug\",\"z\":\"d74afda3e83a644e\",\"g\":\"e9a3b71d40addd8b\",\"name\":\"debug 1\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":740,\"y\":460,\"wires\":",[1238,8519],{},"},{\"id\":\"68fbcdb03e3a5346\",\"type\":\"group\",\"z\":\"d74afda3e83a644e\",\"style\":{\"stroke\":\"#b2b3bd\",\"stroke-opacity\":\"1\",\"fill\":\"#f2f3fb\",\"fill-opacity\":\"0.5\",\"label\":true,\"label-position\":\"nw\",\"color\":\"#32333b\"},\"nodes\":",[1238,8522,8523],{},"\"4f24ab5b5628a1ba\",\"fbf36a7b3c59461d\"",",\"x\":854,\"y\":419,\"w\":392,\"h\":82},{\"id\":\"4f24ab5b5628a1ba\",\"type\":\"catch\",\"z\":\"d74afda3e83a644e\",\"g\":\"68fbcdb03e3a5346\",\"name\":\"\",\"scope\":null,\"uncaught\":false,\"x\":940,\"y\":460,\"wires\":[[\"fbf36a7b3c59461d\"]]},{\"id\":\"fbf36a7b3c59461d\",\"type\":\"debug\",\"z\":\"d74afda3e83a644e\",\"g\":\"68fbcdb03e3a5346\",\"name\":\"debug 6\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"false\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1140,\"y\":460,\"wires\":",[1238,8526],{},"},{\"id\":\"01137d02c4832962\",\"type\":\"group\",\"z\":\"d74afda3e83a644e\",\"name\":\"\",\"style\":{\"label\":true},\"nodes\":",[1238,8529,8530],{},"\"e465d4328d9d42d9\",\"f8269cf412b4200f\",\"2ce801643e01510b\",\"40d48ffb90e5674a\"",",\"x\":274,\"y\":519,\"w\":972,\"h\":82},{\"id\":\"e465d4328d9d42d9\",\"type\":\"inject\",\"z\":\"d74afda3e83a644e\",\"g\":\"01137d02c4832962\",\"name\":\"Insert simulated Data\",\"props\":",[1238,8533,8534],{},"{\"p\":\"payload\"}",",\"repeat\":\"\",\"crontab\":\"\",\"once\":false,\"onceDelay\":0.1,\"topic\":\"\",\"payload\":\"\",\"payloadType\":\"date\",\"x\":420,\"y\":560,\"wires\":[[\"40d48ffb90e5674a\"]]},{\"id\":\"f8269cf412b4200f\",\"type\":\"debug\",\"z\":\"d74afda3e83a644e\",\"g\":\"01137d02c4832962\",\"name\":\"debug 2\",\"active\":true,\"tosidebar\":true,\"console\":false,\"tostatus\":false,\"complete\":\"payload\",\"targetType\":\"msg\",\"statusVal\":\"\",\"statusType\":\"auto\",\"x\":1140,\"y\":560,\"wires\":",[1238,8537],{},"},{\"id\":\"2ce801643e01510b\",\"type\":\"tables-query\",\"z\":\"d74afda3e83a644e\",\"g\":\"01137d02c4832962\",\"name\":\"\",\"query\":\"INSERT INTO public.sensor_readings (sensor_id, timestamp, location, temperature) \\nVALUES ($sensor_id, $timestamp, $location, $temperature);\\n\",\"split\":false,\"rowsPerMsg\":1,\"x\":930,\"y\":560,\"wires\":[[\"f8269cf412b4200f\"]]},{\"id\":\"40d48ffb90e5674a\",\"type\":\"function\",\"z\":\"d74afda3e83a644e\",\"g\":\"01137d02c4832962\",\"name\":\"Generate last 7 days sensor data\",\"func\":\"\u002F\u002F Generate simulated sensor readings for the last 7 days, at every even hour\\nlet now = new Date();\\nlet start = new Date(now.getTime() - (7 * 24 * 60 * 60 * 1000)); \u002F\u002F 7 days ago\\nlet readings = ",[1238,8540],{},";\\n\\n\u002F\u002F Collect all readings first\\nfor (let ts = new Date(start); ts \u003C= now; ts.setHours(ts.getHours() + 1)) {\\n    if (ts.getHours() % 2 === 0) {\\n        readings.push({\\n            queryParameters: {\\n                sensor_id: \"sensor-1\",\\n                timestamp: new Date(ts), \u002F\u002F clone timestamp\\n                location: \"Lab A\",\\n                temperature: Number((20 + Math.random() * 10).toFixed(2)) \u002F\u002F Ensure number type\\n            }\\n        });\\n    }\\n}\\n\\n\u002F\u002F Send them one by one with delay\\nreadings.forEach((reading, i) => {\\n    setTimeout(() => {\\n        node.send(reading);\\n    }, i * 200); \u002F\u002F 200ms delay between messages\\n});\\n\\nreturn null; \u002F\u002F Prevent immediate msg sending\\n\",\"outputs\":1,\"timeout\":0,\"noerr\":0,\"initialize\":\"\",\"finalize\":\"\",\"libs\":",[1238,8543],{},",\"x\":700,\"y\":560,\"wires\":[[\"2ce801643e01510b\"]]},{\"id\":\"89c52680263e6cba\",\"type\":\"global-config\",\"env\":",[1238,8546],{},",\"modules\":{\"@flowfuse\u002Fnr-tables-nodes\":\"0.1.0\"}}","\n{% endrenderFlow %}",[15,8550,8551],{},"After deployment, press the \"Insert simulated Data\" inject button to populate your table with a week's worth of hourly sensor readings. This sample data will help you explore Query node capabilities.",[118,8553,8554],{},[15,8555,8556,8558],{},[53,8557,3077],{}," FlowFuse Tables is currently available for Enterprise users only.",[15,8560,8561],{},"Now, let us test the natural language querying powered by the FlowFuse Expert:",[3084,8563,8564,8567,8570,8573,8576,8582,8585],{},[50,8565,8566],{},"Add an Inject node to your flow",[50,8568,8569],{},"Connect it to your Query node",[50,8571,8572],{},"Open the Query node and locate the new \"Assistant\" codelens",[50,8574,8575],{},"Enter: \"Show me all readings from today\"",[50,8577,3100,8578,8581],{},[53,8579,8580],{},"Ask the FlowFuse Expert",". The FlowFuse Expert will process your natural language request and automatically generate the corresponding SQL query in the Query node's SQL field. Click Done.",[50,8583,8584],{},"Connect a Debug node to see the results",[50,8586,8587],{},"Deploy the flow and click the Inject button to test it.",[15,8589,8590,8594,8597],{},[30,8591],{"alt":8592,"dataZoomable":187,"src":8593},"FlowFuse Expert in Query Node","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fflowfuse-ai-assistance-table-demo.gif",[8595,8596],"br",{},[35,8598,8592],{},[39,8600,8602],{"id":8601},"practical-query-examples","Practical Query Examples",[15,8604,8605],{},"With your sample data in place, here are some immediately useful queries to try:",[143,8607,8609],{"id":8608},"performance-analysis","Performance Analysis",[15,8611,8612,8615,8617],{},[53,8613,8614],{},"Track temperature averages:",[8595,8616],{},"\nPrompt: \"What's the average temperature for this week?\"",[127,8619],{"videoid":8620,"params":8621,"style":8622,"title":8623},"MZxrI9SEegE","rel=0","margin-top: 20px; margin-bottom: 20px; width: 100%; height: 480px;","YouTube video player",[15,8625,8626,8629,8631],{},[53,8627,8628],{},"Identify peak readings:",[8595,8630],{},"\nPrompt: \"Find the highest temperature reading this month\"",[127,8633],{"videoid":8634,"params":8621,"style":8622,"title":8623},"jDIRH2i_1Uk",[143,8636,8638],{"id":8637},"time-based-analysis","Time-Based Analysis",[15,8640,8641,8644,8646],{},[53,8642,8643],{},"Hourly patterns:",[8595,8645],{},"\nPrompt: \"Average temperature per hour today\"",[127,8648],{"videoid":8649,"params":8621,"style":8622,"title":8623},"m4L9ZHE6tdI",[39,8651,8653],{"id":8652},"advanced-query-capabilities","Advanced Query Capabilities",[15,8655,8656],{},"Beyond basic queries, the FlowFuse Expert can handle sophisticated analysis:",[15,8658,8659,8662,8664],{},[53,8660,8661],{},"Complex filtering:",[8595,8663],{},"\nPrompt: \"Show readings where temperature > 20, temperature \u003C 25, and temperature ≠ 22\"",[127,8666],{"videoid":8667,"params":8621,"style":8622,"title":8623},"MtzcbmFg1-4",[15,8669,8670,8673,8675],{},[53,8671,8672],{},"Statistical operations:",[8595,8674],{},"\nPrompt: \"Calculate standard deviation of temperature readings this month\"",[127,8677],{"videoid":8678,"params":8621,"style":8622,"title":8623},"aJ8znXOn9Hc",[15,8680,8681],{},"These examples demonstrate how the FlowFuse Expert simplifies advanced analysis, turning complex database operations into easy, natural-language requests.",[39,8683,8685],{"id":8684},"whats-next","What's Next",[15,8687,8688],{},"The FlowFuse Expert now brings natural language capabilities to database queries in FlowFuse Tables. This removes the complexity of SQL, allowing industrial teams to extract insights using simple conversational commands.",[15,8690,8691],{},"FlowFuse's mission has always been to democratize industrial automation and reduce complexity for engineers and operational teams. As part of this commitment, more AI-powered features are on the roadmap to simplify industrial workflows even further.",[15,8693,8694],{},"Ready to transform how your team works with data? [Book a demo]({% include \"sign-up-url.njk\" %}) and see how FlowFuse makes building industrial applications simple and accessible.",{"title":187,"searchDepth":188,"depth":188,"links":8696},[8697,8698,8699,8703,8704],{"id":8454,"depth":191,"text":8455},{"id":8475,"depth":191,"text":8476},{"id":8601,"depth":191,"text":8602,"children":8700},[8701,8702],{"id":8608,"depth":196,"text":8609},{"id":8637,"depth":196,"text":8638},{"id":8652,"depth":191,"text":8653},{"id":8684,"depth":191,"text":8685},"2025-09-18","Learn the easiest way to connect to your database and get data, no coding knowledge required.","\u002Fblog\u002F2025\u002F09\u002Fimages\u002Fflowfuse-assistant-query-node.png",{"keywords":8709,"excerpt":8710},"FlowFuse Tables, SQL natural language, database queries, Query node, Node-RED, sensor data, temperature monitoring, industrial automation, low-code platform, data analysis",{"type":12,"value":8711},[8712],[15,8713,8451],{},"\u002Fblog\u002F2025\u002F09\u002Fai-assistant-flowfuse-tables",{"title":8445,"description":8706},{"loc":8714},"blog\u002F2025\u002F09\u002Fai-assistant-flowfuse-tables","A faster, more intuitive way to get data from your tables without writing a single line of SQL.",[223,224],"duAsYMAy-O5CyqkT0IiLtoZBNGkg6OdsIoNYOQwC8bE",{"id":8722,"title":8723,"authors":8724,"body":8725,"cta":3,"date":8941,"description":8942,"extension":207,"image":8943,"lastUpdated":3,"meta":8944,"navigation":216,"path":8949,"seo":8950,"sitemap":8951,"stem":8952,"subtitle":8953,"tags":8954,"tldr":3,"video":3,"__hash__":8955},"blog\u002Fblog\u002F2025\u002F08\u002Fflowfuse-release-2-21.md","FlowFuse 2.21: AI-Assisted SQL, Low-Code Custom Nodes, and Remote Instance Performance Insights",[6007],{"type":12,"value":8726,"toc":8926},[8727,8730,8734,8743,8751,8754,8758,8767,8770,8773,8777,8780,8806,8809,8813,8821,8824,8827,8830,8834,8843,8851,8854,8858,8867,8870,8874,8883,8886,8889,8892,8894,8901,8906,8910,8912,8914,8916,8918,8920],[15,8728,8729],{},"It's been a very busy release and we have many great new features available on FlowFuse that will provide a better Node-RED development experience, makes it easier to develop and interface with your Unified Namespace, provide more insight into Remote Instance performance and new low-code tooling for building your own custom Node-RED nodes.",[39,8731,8733],{"id":8732},"assistant-functionality-in-tables-nodes","Assistant Functionality in Tables Nodes",[15,8735,8736,8740],{},[30,8737],{"alt":8738,"src":8739},"Gif showing FlowFuse Expert in Tables","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Ftables.gif",[35,8741,8742],{},"FlowFuse Expert in Tables recognizes table schema and turns natural language prompts into SQL queries",[15,8744,8745,8746,8750],{},"Building on our successful ",[22,8747,8749],{"href":8748},"\u002Fblog\u002F2025\u002F07\u002Fflowfuse-release-2-20\u002F","Tables launch in 2.20",", we've now integrated AI assistance directly into our Tables nodes. This lowers the barrier for working with databases, reducing the dependency on SQL knowledge. With this, you can type a natural language prompt that will be interpreted in light of the structure of tables in your FlowFuse Tables, which enables an AI-supported autocomplete and assists with writing SQL specifically for connected FlowFuse tables.",[15,8752,8753],{},"This integration makes working with FlowFuse Tables even more accessible, allowing developers to leverage AI guidance for database operations without requiring deep SQL expertise.",[39,8755,8757],{"id":8756},"summarize-snapshots","Summarize Snapshots",[15,8759,8760,8764],{},[30,8761],{"alt":8762,"src":8763,"dataZoomable":187},"Screenshot showing snapshot summarization feature","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Fsnapshot.png",[35,8765,8766],{},"New snapshot summarization provides clear, AI-generated descriptions of changes between versions",[15,8768,8769],{},"Managing instance versions becomes more intuitive with our new Snapshot Summary feature. When creating snapshots, FlowFuse can now automatically generate intelligent summaries that describes the changes introduced. This saves you time, and makes it much easier for teams to understand project evolution and quickly identify the right version for deployment or rollback scenarios.",[15,8771,8772],{},"Available for Pro and Enterprise.",[39,8774,8776],{"id":8775},"team-broker-nodes","Team Broker Nodes",[15,8778,8779],{},"Easily publish and subscribe to topics in the FlowFuse Broker using new Team Broker nodes. Send a message from a Node-RED flow directly into the FlowFuse Broker. These new nodes extend the Unified Namespace capabilities of FlowFuse and provide:",[47,8781,8782,8788,8794,8800],{},[50,8783,8784,8787],{},[53,8785,8786],{},"Publish Node",": Send messages to any topic on your team broker with configurable retention settings",[50,8789,8790,8793],{},[53,8791,8792],{},"Subscribe Node",": Receive messages from specified topics with flexible output formatting",[50,8795,8796,8799],{},[53,8797,8798],{},"Auto-Configuration",": Nodes automatically use your team's broker settings",[50,8801,8802,8805],{},[53,8803,8804],{},"TypedInput Support",": Dynamic topic configuration using message properties or static values",[15,8807,8808],{},"These nodes make working between Node-RED and the FlowFuse Broker much simpler and easier.",[39,8810,8812],{"id":8811},"low-code-custom-node-development","Low-Code Custom Node Development",[15,8814,8815,8820],{},[22,8816,8819],{"href":8817,"rel":8818},"https:\u002F\u002Fnodered.org\u002Fdocs\u002Fuser-guide\u002Feditor\u002Fworkspace\u002Fsubflows",[445],"Subflows"," are a great way in Node-RED to build custom nodes, all within the Node-RED Editor, and without having to write any code. The limitation of Subflows though is that they're constrained to just one Instance of Node-RED, they cannot be shared across your whole team. That is no longer the case.",[15,8822,8823],{},"We've now introduced the Subflow exporter which provides a low-code and intuitive way to create and manage custom nodes.",[15,8825,8826],{},"From the sidebar in your Node-RED Editor, you can now very easily create and manage custom nodes, without writing code or having to create and manage your own version control infrastructure. Simply create a flow in Node-RED, convert it to a subflow, fill out the package details for your new custom node and hit \"Publish\". Now your new node is available for all to install across your FlowFuse team.",[15,8828,8829],{},"Available for Enterprise customers only.",[39,8831,8833],{"id":8832},"remote-instance-observability","Remote Instance Observability",[15,8835,8836,8840],{},[30,8837],{"alt":8838,"src":8839},"Screenshot of remote instance monitoring interface","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Fremote.png",[35,8841,8842],{},"Remote Instance monitoring in the Performance view provides usage insights",[15,8844,8845,8846,8850],{},"Following the success of our ",[22,8847,8849],{"href":8848},"\u002Fblog\u002F2025\u002F06\u002Fflowfuse-release-2-18\u002F#enhanced-observability-for-better-performance-management","Hosted Instance performance monitoring",", we've extended observability capabilities to include Remote Instances too. This extension gives insight into CPU usage and memory usage for your remote instances.",[15,8852,8853],{},"This enhancement is particularly valuable for industrial deployments where remote instances run critical processes across multiple locations.",[39,8855,8857],{"id":8856},"blueprint-energy-monitoring-dashboard","Blueprint: Energy Monitoring Dashboard",[15,8859,8860,8864],{},[30,8861],{"alt":8862,"src":8863},"Screenshot of energy monitoring dashboard","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Fenergy-monitoring.png",[35,8865,8866],{},"Energy Monitoring Dashboard provides realtime usage and cost insights",[15,8868,8869],{},"This Blueprint provides a real-time energy monitoring dashboard template for industrial facilities. It features live consumption tracking, cost analytics, spike detection, and historical trending with an integrated energy rate display. Perfect for demonstrating IoT energy management capabilities, and with Node-RED, it is fully customizable.",[39,8871,8873],{"id":8872},"annual-billing-option","Annual Billing Option",[15,8875,8876,8880],{},[30,8877],{"alt":8878,"src":8879,"dataZoomable":187},"Screenshot of annual billing selection interface","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Fannual-billing.png",[35,8881,8882],{},"New annual billing options provide cost savings and simplified budget planning",[15,8884,8885],{},"FlowFuse Cloud customers on Starter and Pro plans can now choose to subscribe on a yearly basis and receive a free month for doing so. This allows teams to save money compared to a monthly subscription and lock in current pricing.",[39,8887,8888],{"id":8684},"What's Next?",[15,8890,8891],{},"For the next release, we're working on features that will enable you to connect your own AI models with Node-RED and FlowFuse, paving the way to create AI-supported automations in your applications.  We're also planning on pushing a lot of performance updates to Dashboard, and to make it even easier to build your own applications on FlowFuse. We're excited about it -- stay tuned!",[39,8893,380],{"id":379},[15,8895,8896,8897,474],{},"For a complete list of everything included in our 2.21 release, check out the ",[22,8898,473],{"href":8899,"rel":8900},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.21.0",[445],[15,8902,5942,8903,474],{},[22,8904,5947],{"href":5945,"rel":8905},[445],[15,8907,6129,8908,6134],{},[22,8909,6133],{"href":6132},[39,8911,5954],{"id":5953},[143,8913,5958],{"id":5957},[15,8915,5961],{},[15,8917,5964],{},[143,8919,5968],{"id":5967},[15,8921,5971,8922,5976,8924,474],{},[22,8923,5975],{"href":5974},[22,8925,5980],{"href":5979},{"title":187,"searchDepth":188,"depth":188,"links":8927},[8928,8929,8930,8931,8932,8933,8934,8935,8936,8937],{"id":8732,"depth":191,"text":8733},{"id":8756,"depth":191,"text":8757},{"id":8775,"depth":191,"text":8776},{"id":8811,"depth":191,"text":8812},{"id":8832,"depth":191,"text":8833},{"id":8856,"depth":191,"text":8857},{"id":8872,"depth":191,"text":8873},{"id":8684,"depth":191,"text":8888},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":8938},[8939,8940],{"id":5957,"depth":196,"text":5958},{"id":5967,"depth":196,"text":5968},"2025-08-28","Introducing FlowFuse Expert functionality in Tables to do natural language queries of your databases, Remote Instance observability to improve performance monitoring, Team Broker nodes to make MQTT even easier to work with, a new Energy Monitoring Blueprint, Annual Billing for Self-Service, AI-Generated Snapshot Summaries, and new subflow version control to provide low-code development of custom nodes","\u002Fblog\u002F2025\u002F08\u002Fimages\u002Frelease-2.21.png",{"excerpt":8945},{"type":12,"value":8946},[8947],[15,8948,8729],{},"\u002Fblog\u002F2025\u002F08\u002Fflowfuse-release-2-21",{"title":8723,"description":8942},{"loc":8949},"blog\u002F2025\u002F08\u002Fflowfuse-release-2-21","Introducing FlowFuse Expert functionality in Tables to do natural language queries of your databases, Remote Instance observability to improve performance monitoring, Team Broker nodes to make MQTT even easier to work with, a new Energy Monitoring Blueprint, Annual Billing for Self-Service, AI-Generated Snapshot Summaries, and new subflow version control to provide low-code development of custom nodes.",[223,529,530,224],"8UWySwr0H96hTqmMpomaTPTw1lKXOno5dUUK15wKxzk",{"id":8957,"title":8958,"authors":8959,"body":8960,"cta":3,"date":9153,"description":9154,"extension":207,"image":9155,"lastUpdated":3,"meta":9156,"navigation":216,"path":9161,"seo":9162,"sitemap":9163,"stem":9164,"subtitle":9165,"tags":9166,"tldr":3,"video":3,"__hash__":9167},"blog\u002Fblog\u002F2025\u002F07\u002Fflowfuse-release-2-20.md","FlowFuse 2.20: AI-Assisted Node-RED & New Database Service",[6007],{"type":12,"value":8961,"toc":9138},[8962,8965,8969,8977,8980,8983,8986,8990,8998,9001,9005,9014,9017,9020,9024,9030,9033,9036,9044,9048,9057,9060,9086,9089,9093,9096,9098,9101,9104,9106,9113,9118,9122,9124,9126,9128,9130,9132],[15,8963,8964],{},"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.",[39,8966,8968],{"id":8967},"introducing-flowfuse-tables","Introducing: FlowFuse Tables",[15,8970,8971,8975],{},[30,8972],{"alt":8973,"dataZoomable":187,"src":8974},"A screenshot of the new \"Tables\" view, now available in FlowFuse","\u002Fblog\u002F2025\u002F07\u002Fimages\u002Ftables-ui-screenshot.png",[35,8976,8973],{},[15,8978,8979],{},"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.",[15,8981,8982],{},"FlowFuse Tables eliminates the complexity of setting up and managing separate database infrastructure, allowing you to focus on building applications that drive operational efficiency.",[15,8984,8985],{},"FlowFuse Tables is available now for all Enterprise users running on FlowFuse Cloud.",[143,8987,8989],{"id":8988},"new-node-query","New Node: Query",[15,8991,8992,8996],{},[30,8993],{"alt":8994,"dataZoomable":187,"src":8995},"A flow in Node-RED that uses the new FlowFuse \"Query\" node","\u002Fblog\u002F2025\u002F07\u002Fimages\u002Ftables-query-node.png",[35,8997,8994],{},[15,8999,9000],{},"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.",[39,9002,9004],{"id":9003},"ai-assisted-node-red-with-smart-suggestions","AI-Assisted Node-RED with Smart Suggestions",[15,9006,9007,9011],{},[30,9008],{"alt":9009,"dataZoomable":187,"src":9010},"GIF of Smart Suggestions","\u002Fblog\u002F2025\u002F07\u002Fimages\u002Fsmart-suggestion.gif",[35,9012,9013],{},"GIF of Smart Suggestions in Action",[15,9015,9016],{},"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.",[15,9018,9019],{},"This work extends the functionality of the in-built FlowFuse Expert and it's MCP server that runs behind the Node-RED Editor to provide power additional development enhancements.",[39,9021,9023],{"id":9022},"new-blueprint-agentic-ai-with-retrieval-augmented-generation","New Blueprint: Agentic AI with Retrieval Augmented Generation",[15,9025,9026],{},[9027,9028],"video",{"src":9029,"controls":216},"https:\u002F\u002Fwebsite-data.s3.eu-west-1.amazonaws.com\u002FBlueprint+-+Open+AI+RAG.mp4",[15,9031,9032],{},"The new RAG (Retrieval Augmented Generation) Blueprint enables you to train your own LLM agents, combining your own data with natural language capabilities.",[15,9034,9035],{},"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.",[15,9037,9038,9039],{},"The RAG Blueprint makes it easy to create intelligent agents that leverage your organizational knowledge without requiring deep AI expertise. ",[22,9040,9043],{"href":9041,"rel":9042},"https:\u002F\u002Fflowfuse.com\u002Fblueprints\u002Fai\u002Frag-chat-agent\u002F",[445],"Try it out for yourself here.",[39,9045,9047],{"id":9046},"refined-applications-page","Refined Applications Page",[15,9049,9050,9054],{},[30,9051],{"alt":9052,"dataZoomable":187,"src":9053},"Screenshot of New Applications Page","\u002Fblog\u002F2025\u002F07\u002Fimages\u002Fapplications-redesign.png",[35,9055,9056],{},"Screenshot of Redesigned Applications Page",[15,9058,9059],{},"With the new FlowFuse Home page in place, we have greatly streamlined the Applications page. The new structure includes:",[47,9061,9062,9068,9074,9080],{},[50,9063,9064,9067],{},[53,9065,9066],{},"Streamlined Navigation",": Applications now appear under Instances in the navigation hierarchy",[50,9069,9070,9073],{},[53,9071,9072],{},"Reduced Cognitive Load",": Eliminated the overwhelming number of buttons and links in the previous design",[50,9075,9076,9079],{},[53,9077,9078],{},"Focus on Important Information",": The newly refined design focusses on giving you a clear overview of the status of your Hosted and Remote Instances, split by Application.",[50,9081,9082,9085],{},[53,9083,9084],{},"Performance Optimizations",": The above has also lead to faster page loading and improved responsiveness",[15,9087,9088],{},"This redesign creates a more intuitive workflow that aligns with how teams actually use FlowFuse, reducing clicks and improving productivity.",[39,9090,9092],{"id":9091},"more-powerful-small-instances","More Powerful \"Small\" Instances",[15,9094,9095],{},"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.",[39,9097,8888],{"id":8684},[15,9099,9100],{},"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 FlowFuse Expert will gain more sophisticated workflow automation features.",[15,9102,9103],{},"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!",[39,9105,380],{"id":379},[15,9107,9108,9109,474],{},"For a complete list of everything included in our 2.20 release, check out the ",[22,9110,473],{"href":9111,"rel":9112},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.20.0",[445],[15,9114,5942,9115,474],{},[22,9116,5947],{"href":5945,"rel":9117},[445],[15,9119,6129,9120,6134],{},[22,9121,6133],{"href":6132},[39,9123,5954],{"id":5953},[143,9125,5958],{"id":5957},[15,9127,5961],{},[15,9129,5964],{},[143,9131,5968],{"id":5967},[15,9133,5971,9134,5976,9136,474],{},[22,9135,5975],{"href":5974},[22,9137,5980],{"href":5979},{"title":187,"searchDepth":188,"depth":188,"links":9139},[9140,9143,9144,9145,9146,9147,9148,9149],{"id":8967,"depth":191,"text":8968,"children":9141},[9142],{"id":8988,"depth":196,"text":8989},{"id":9003,"depth":191,"text":9004},{"id":9022,"depth":191,"text":9023},{"id":9046,"depth":191,"text":9047},{"id":9091,"depth":191,"text":9092},{"id":8684,"depth":191,"text":8888},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":9150},[9151,9152],{"id":5957,"depth":196,"text":5958},{"id":5967,"depth":196,"text":5968},"2025-07-31","Introducing FlowFuse Tables for data storage, Tables nodes for dashboard visualization, Smart Suggestions in the Node-RED editor, More Powerful Starter tier, Retrieval Augmented Generation Blueprint for intelligent applications, and a redesigned Applications page for better workspace management.","\u002Fblog\u002F2025\u002F07\u002Fimages\u002Frelease-2-20.png",{"excerpt":9157},{"type":12,"value":9158},[9159],[15,9160,8964],{},"\u002Fblog\u002F2025\u002F07\u002Fflowfuse-release-2-20",{"title":8958,"description":9154},{"loc":9161},"blog\u002F2025\u002F07\u002Fflowfuse-release-2-20","Introducing 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.",[223,529,530,224],"AjKsBszu8JqUGjOuhbK86IhS5POL3x2NetmI6fVIano",{"id":9169,"title":9170,"authors":9171,"body":9172,"cta":3,"date":9334,"description":9335,"extension":207,"image":9336,"lastUpdated":3,"meta":9337,"navigation":216,"path":9342,"seo":9343,"sitemap":9344,"stem":9345,"subtitle":9335,"tags":9346,"tldr":3,"video":3,"__hash__":9347},"blog\u002Fblog\u002F2025\u002F06\u002Fflowfuse-release-2-18.md","FlowFuse 2.18: Smarter Monitoring, AI Integration, Improved DevOps, and a preview of exciting things to come",[6007],{"type":12,"value":9173,"toc":9322},[9174,9177,9181,9190,9193,9196,9200,9208,9211,9214,9217,9225,9229,9232,9235,9242,9246,9249,9258,9261,9265,9268,9271,9274,9280,9282,9289,9295,9300,9303,9305,9307,9314,9316,9319],[15,9175,9176],{},"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.",[39,9178,9180],{"id":9179},"enhanced-observability-for-better-performance-management","Enhanced Observability for Better Performance Management",[15,9182,9183,9187],{},[30,9184],{"alt":9185,"src":9186},"Screenshot of Performance feature","\u002Fblog\u002F2025\u002F06\u002Fimages\u002Fobservability1.png",[35,9188,9189],{},"Screenshot of Performance Feature",[15,9191,9192],{},"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.",[15,9194,9195],{},"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.",[39,9197,9199],{"id":9198},"blueprint-openai-llm-with-chat-agent","Blueprint: OpenAI LLM with Chat Agent",[15,9201,9202,9205],{},[9027,9203],{"src":9204,"controls":216},"https:\u002F\u002Fwebsite-data.s3.eu-west-1.amazonaws.com\u002FBlueprint+-+Open+AI+Chat.mp4",[35,9206,9207],{},"Video of OpenAI LLM Blueprint demo",[15,9209,9210],{},"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.",[15,9212,9213],{},"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.",[15,9215,9216],{},"Check out the video demo to see it in action, featuring the agent connected to a worldmap node!",[15,9218,9219,9220],{},"To put this Blueprint to use, check out the Blueprint page for ",[22,9221,9224],{"href":9222,"rel":9223},"https:\u002F\u002Fflowfuse.com\u002Fblueprints\u002Fai\u002Fllm-chat-agent\u002F",[445],"OpenAI LLM Chat Agent.",[39,9226,9228],{"id":9227},"complete-git-integration-with-pull-support","Complete Git Integration with Pull Support",[15,9230,9231],{},"Building on our previous Git push functionality, we've now added Git pull support, completing the core Git integration experience within FlowFuse.",[15,9233,9234],{},"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.",[15,9236,9237,9238,474],{},"More details are available in the ",[22,9239,9241],{"href":9240},"\u002Fchangelog\u002F2025\u002F06\u002Fgit-integration\u002F","Git Integration changelog",[39,9243,9245],{"id":9244},"self-hosted-blueprint-support","Self-Hosted Blueprint Support",[15,9247,9248],{},"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.",[15,9250,9251,9252,9257],{},"Self-hosted installations will automatically pull down the ",[22,9253,9256],{"href":9254,"rel":9255},"https:\u002F\u002Fflowfuse.com\u002Fblueprints\u002F",[445],"blueprint library",", and will stay up to date when we publish new blueprints.",[15,9259,9260],{},"This update ensures that all FlowFuse users, regardless of their deployment model, can benefit from our growing library of pre-built solutions.",[39,9262,9264],{"id":9263},"where-are-we-headed","Where are we headed?",[15,9266,9267],{},"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.",[15,9269,9270],{},"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.",[15,9272,9273],{},"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.",[15,9275,9276],{},[30,9277],{"alt":9278,"src":9279},"Preview of AI in Node-RED Editor","\u002Fblog\u002F2025\u002F06\u002Fimages\u002FAI_preview.gif",[39,9281,380],{"id":379},[15,9283,9284,9285,474],{},"For a full list of everything that went into our 2.18 release, you can check out the ",[22,9286,473],{"href":9287,"rel":9288},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002F",[445],[15,9290,9291,9292,474],{},"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 ",[22,9293,5947],{"href":5945,"rel":9294},[445],[15,9296,9297,9298,474],{},"Which feature do you think you're most likely to use? Email me directly and let me know! You can reach me at ",[22,9299,6133],{"href":6132},[15,9301,9302],{},"Together, we can make FlowFuse better with each release!",[39,9304,5954],{"id":5953},[143,9306,5968],{"id":5967},[15,9308,9309,9310,5976,9312,474],{},"We're confident you can have self managed FlowFuse running locally in under 30 minutes. You can install FlowFuse using ",[22,9311,5975],{"href":5974},[22,9313,5980],{"href":5979},[143,9315,5958],{"id":5957},[15,9317,9318],{},"The quickest and easiest way to get started with FlowFuse is FlowFuse Cloud.",[15,9320,9321],{},"[Get started for free]({% include \"sign-up-url.njk\" %}) now, and you'll have your own Node-RED instances running in the Cloud within minutes.",{"title":187,"searchDepth":188,"depth":188,"links":9323},[9324,9325,9326,9327,9328,9329,9330],{"id":9179,"depth":191,"text":9180},{"id":9198,"depth":191,"text":9199},{"id":9227,"depth":191,"text":9228},{"id":9244,"depth":191,"text":9245},{"id":9263,"depth":191,"text":9264},{"id":379,"depth":191,"text":380},{"id":5953,"depth":191,"text":5954,"children":9331},[9332,9333],{"id":5967,"depth":196,"text":5968},{"id":5957,"depth":196,"text":5958},"2025-06-05","Monitor and improve instance performance, run AI chat in your Dashboard, Git pull, and more.","\u002Fblog\u002F2025\u002F06\u002Fimages\u002Frelease-2-18.png",{"excerpt":9338},{"type":12,"value":9339},[9340],[15,9341,9176],{},"\u002Fblog\u002F2025\u002F06\u002Fflowfuse-release-2-18",{"title":9170,"description":9335},{"loc":9342},"blog\u002F2025\u002F06\u002Fflowfuse-release-2-18",[223,529,530,224],"ncR93KLRKpL8WizzngXf3ZGiIaBvBkUIqQyqxNObK8o",{"id":9349,"title":9350,"authors":9351,"body":9352,"cta":3,"date":9444,"description":9445,"extension":207,"image":9446,"lastUpdated":3,"meta":9447,"navigation":216,"path":9456,"seo":9457,"sitemap":9458,"stem":9459,"subtitle":9460,"tags":9461,"tldr":3,"video":3,"__hash__":9463},"blog\u002Fblog\u002F2024\u002F07\u002Fevolution-of-technology-impact-on-job-roles-and-companies.md","Evolution of Technology: Impact on Job Roles and Companies",[10],{"type":12,"value":9353,"toc":9437},[9354,9362,9366,9369,9372,9375,9379,9382,9385,9388,9392,9400,9403,9407,9410,9426,9429,9432,9434],[15,9355,9356,9357],{},"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: ",[35,9358,9359],{},[53,9360,9361],{},"\"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\"",[39,9363,9365],{"id":9364},"historical-examples-of-technological-disruption-and-business-impact","Historical Examples of Technological Disruption and Business Impact",[15,9367,9368],{},"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.",[15,9370,9371],{},"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.",[15,9373,9374],{},"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.",[39,9376,9378],{"id":9377},"current-trends-ai-low-code-tools-and-strategic-adaptation","Current Trends: AI, Low-Code Tools, and Strategic Adaptation",[15,9380,9381],{},"Today, we are witnessing a new era of technological disruption driven by artificial intelligence (AI) and low-code\u002Fno-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.",[15,9383,9384],{},"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.",[15,9386,9387],{},"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.",[39,9389,9391],{"id":9390},"the-importance-of-adaptability-and-strategic-vision","The Importance of Adaptability and Strategic Vision",[15,9393,9394,9395,474],{},"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 ",[22,9396,9399],{"href":9397,"rel":9398},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=X3NASGb5m8s&t=2s",[445],"Why Xerox failed",[15,9401,9402],{},"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.",[39,9404,9406],{"id":9405},"flowfuse-the-perfect-combo-of-low-code-and-ai","FlowFuse: The perfect combo of Low-code and AI",[15,9408,9409],{},"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.",[15,9411,9412,9413,1423,9417,9421,9422,9425],{},"With the capability to integrate over 5000 applications, protocols, and technologies, FlowFuse empowers businesses to innovate and streamline operations.\nRecently, FlowFuse has integrated AI features like the ",[22,9414,9416],{"href":9415},"\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node\u002F","Function GPT",[22,9418,9420],{"href":9419},"\u002Fblog\u002F2023\u002F11\u002Fchatgpt-gpt\u002F","Node-RED Builder"," and the ",[22,9423,714],{"href":9424},"\u002Fchangelog\u002F2024\u002F07\u002Fflowfuse-assistant\u002F",". 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.",[15,9427,9428],{},"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.",[15,9430,9431],{},"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.",[39,9433,8391],{"id":8390},[15,9435,9436],{},"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.",{"title":187,"searchDepth":188,"depth":188,"links":9438},[9439,9440,9441,9442,9443],{"id":9364,"depth":191,"text":9365},{"id":9377,"depth":191,"text":9378},{"id":9390,"depth":191,"text":9391},{"id":9405,"depth":191,"text":9406},{"id":8390,"depth":191,"text":8391},"2024-07-23","Discover how technology evolution, from historical disruptions to AI and low-code tools like Node-RED, reshapes job roles and enhances business operations","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fevolution-of-technology.png",{"excerpt":9448},{"type":12,"value":9449},[9450],[15,9451,9356,9452],{},[35,9453,9454],{},[53,9455,9361],{},"\u002Fblog\u002F2024\u002F07\u002Fevolution-of-technology-impact-on-job-roles-and-companies",{"title":9350,"description":9445},{"loc":9456},"blog\u002F2024\u002F07\u002Fevolution-of-technology-impact-on-job-roles-and-companies","The Role of FlowFuse in the Modern Technological Landscape",[8441,223,9462,224],"low-code","WPAyxmtKKPx7SFWEi1pPxIGxjB4Uw9rOftJ853PyjTs",{"id":9465,"title":9466,"authors":9467,"body":9469,"cta":3,"date":9767,"description":9768,"extension":207,"image":9769,"lastUpdated":3,"meta":9770,"navigation":216,"path":9784,"seo":9785,"sitemap":9786,"stem":9787,"subtitle":9788,"tags":9789,"tldr":3,"video":3,"__hash__":9794},"blog\u002Fblog\u002F2024\u002F07\u002Fflowfuse-2-6-release.md","FlowFuse 2.6: AI Infused Node-RED, Persistent File Storage & Lots More",[9468],"joe-pavitt",{"type":12,"value":9470,"toc":9748},[9471,9490,9494,9501,9505,9514,9521,9529,9537,9540,9544,9547,9550,9559,9566,9575,9578,9581,9585,9588,9591,9594,9603,9611,9615,9619,9622,9630,9633,9637,9645,9652,9656,9665,9668,9675,9679,9686,9691,9693,9697,9699,9706,9708,9711,9715,9723,9730,9734],[15,9472,9473,9474,1423,9479,9484,9485,474],{},"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 ",[22,9475,9478],{"href":9476,"rel":9477},"https:\u002F\u002Fflowfuse.com\u002Fhandbook\u002Fengineering\u002Fproduct\u002Fstrategy\u002F#simplified-hosting",[445],"Simplified Hosting",[22,9480,9483],{"href":9481,"rel":9482},"https:\u002F\u002Fflowfuse.com\u002Fhandbook\u002Fengineering\u002Fproduct\u002Fstrategy\u002F#low-code",[445],"Low-Code"," plans from our ",[22,9486,9489],{"href":9487,"rel":9488},"https:\u002F\u002Fflowfuse.com\u002Fhandbook\u002Fengineering\u002Fproduct\u002Fstrategy\u002F",[445],"Product Strategy",[39,9491,9493],{"id":9492},"improving-the-node-red-experience","Improving the Node-RED Experience",[15,9495,9496,9497,9500],{},"Whilst a big part of FlowFuse is the ability to run Node-RED, we're also focussing on how to improve the experience of ",[35,9498,9499],{},"building"," with Node-RED on FlowFuse too.",[143,9502,9504],{"id":9503},"ai-infused-node-red","AI-Infused Node-RED",[15,9506,9507,9511],{},[30,9508],{"alt":9509,"dataZoomable":187,"src":9510},"Screenshot showing the \"FlowFuse Expert\" dialog box","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fassistant-dialog-function-node-builder.png",[35,9512,9513],{},"Screenshot of an example instruction sent to FlowFuse Expert.",[15,9515,9516,9517,9520],{},"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 ",[53,9518,9519],{},"Node-RED Assistant"," in both the Editor toolbar, and the function nodes:",[15,9522,9523,9527],{},[30,9524],{"alt":9525,"dataZoomable":187,"src":9526},"Screenshot showing the \"FlowFuse Expert\" button available in the Editor Toolbar","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fassistant-toolbar.png",[35,9528,9525],{},[15,9530,9531,9535],{},[30,9532],{"alt":9533,"dataZoomable":187,"src":9534},"Screenshot showing the \"Ask the FlowFuse Expert\" button available in the function node","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fassistant-function-node-inline-code-lens.png",[35,9536,9533],{},[15,9538,9539],{},"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.",[143,9541,9543],{"id":9542},"immersive-editor-experience","Immersive Editor Experience",[15,9545,9546],{},"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.",[15,9548,9549],{},"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\".",[15,9551,9552,9556],{},[30,9553],{"alt":9554,"dataZoomable":187,"src":9555},"Screenshot showing the \"Immersive Editor\" view","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fimmersive-editor.png",[35,9557,9558],{},"Screenshot showing the \"Immersive Editor\" view, where the FlowFuse navigation is available as a floating bar.",[15,9560,9561,9562,9565],{},"Now, the instances tabs are all available in the ",[35,9563,9564],{},"same"," view as the Editor.",[15,9567,9568,9572],{},[30,9569],{"alt":9570,"dataZoomable":187,"src":9571},"Screenshot showing the \"Collapsed\" FlowFuse bar","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fimmersive-editor-collapsed.png",[35,9573,9574],{},"Screenshot showing the \"FlowFuse\" button at the bottom of the Node-RED Editor.",[15,9576,9577],{},"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.",[15,9579,9580],{},"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.",[39,9582,9584],{"id":9583},"persistent-file-storage","Persistent File Storage",[15,9586,9587],{},"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.",[15,9589,9590],{},"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.",[15,9592,9593],{},"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.",[15,9595,9596,9597,9602],{},"This unlocks lots of new capabilities using nodes from the community. For example, the ",[22,9598,9601],{"href":9599,"rel":9600},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-node-sqlite",[445],"SQLite"," nodes can be used to quickly add a locally managed database to store your data in.",[15,9604,9605,9606,9610],{},"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 ",[22,9607,9609],{"href":6315,"rel":9608},[445],"in touch"," and we can help move you over.",[39,9612,9614],{"id":9613},"other-highlights","Other Highlights",[143,9616,9618],{"id":9617},"compact-applications-view","Compact Applications View",[15,9620,9621],{},"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.",[15,9623,9624,9628],{},[30,9625],{"alt":9626,"dataZoomable":187,"src":9627},"Screenshot of new \"Applications\" view for a given Team in FlowFuse.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fcompact-applications-view.png",[35,9629,9626],{},[15,9631,9632],{},"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.",[143,9634,9636],{"id":9635},"importexport-blueprints","Import\u002FExport Blueprints",[15,9638,9639,9643],{},[30,9640],{"alt":9641,"dataZoomable":187,"src":9642},"Screenshot showing the admin page for \"Flow Blueprints\" with the new \"Import\"\u002F\"Export\" buttons","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fblueprint-import-export.png",[35,9644,9641],{},[15,9646,9647,9648,474],{},"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 ",[22,9649,9651],{"href":9254,"rel":9650},[445],"Blueprint Library",[143,9653,9655],{"id":9654},"multi-line-environment-variables","Multi-line Environment Variables",[15,9657,9658,9662],{},[30,9659],{"alt":9660,"dataZoomable":187,"src":9661},"Screenshot showing the \"Environment Variables\" table in the Instance's Settings","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Fmultiline-env-vars.png",[35,9663,9664],{},"Screenshot showing the \"Environment Variables\" table in an Instance's Settings",[15,9666,9667],{},"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.",[15,9669,9670,9671,9674],{},"We've also improved the ",[76,9672,9673],{},".env"," importing to support the use of multi-line values too.",[143,9676,9678],{"id":9677},"and-much-more","And Much More...",[15,9680,9681,9682,474],{},"For a full list of everything that went into our 2.6 release, you can check out the ",[22,9683,473],{"href":9684,"rel":9685},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fflowfuse\u002Freleases\u002Ftag\u002Fv2.6.0",[445],[15,9687,9291,9688,474],{},[22,9689,5947],{"href":5945,"rel":9690},[445],[15,9692,9302],{},[39,9694,9696],{"id":9695},"try-it-out","Try it out",[143,9698,5968],{"id":5967},[15,9700,9701,9702,474],{},"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 ",[22,9703,9705],{"href":9704},"\u002Fdocs\u002Finstall\u002Fintroduction\u002F","here",[143,9707,5958],{"id":5957},[15,9709,9710],{},"The quickest and easiest way to get started with FlowFuse is on our own hosted instance, FlowFuse Cloud: [Get started for free]({% include \"sign-up-url.njk\" %}) now, and you'll have your own Node-RED instances running in the Cloud within minutes.",[39,9712,9714],{"id":9713},"upgrading-flowfuse","Upgrading FlowFuse",[15,9716,9717,9718,9722],{},"If you're using [FlowFuse Cloud](",[9719,9720],"binding",{"value":9721},"site.appURL","), 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.",[15,9724,9725,9726,474],{},"If you installed a previous version of FlowFuse and want to upgrade, our documentation provides a\nguide for ",[22,9727,9729],{"href":9728},"\u002Fdocs\u002Fupgrade\u002F","upgrading your FlowFuse instance",[39,9731,9733],{"id":9732},"getting-help","Getting help",[15,9735,9736,9737,9741,9742,9747],{},"Please check FlowFuse's ",[22,9738,9740],{"href":9739},"\u002Fdocs\u002F","documentation"," as the answers to many questions are covered there. Additionally you can go to the ",[22,9743,9746],{"href":9744,"rel":9745},"https:\u002F\u002Fdiscourse.nodered.org\u002Fc\u002Fvendors\u002Fflowfuse\u002F24",[445],"community forum"," if you have\nany feedback or feature requests.",{"title":187,"searchDepth":188,"depth":188,"links":9749},[9750,9754,9755,9761,9765,9766],{"id":9492,"depth":191,"text":9493,"children":9751},[9752,9753],{"id":9503,"depth":196,"text":9504},{"id":9542,"depth":196,"text":9543},{"id":9583,"depth":191,"text":9584},{"id":9613,"depth":191,"text":9614,"children":9756},[9757,9758,9759,9760],{"id":9617,"depth":196,"text":9618},{"id":9635,"depth":196,"text":9636},{"id":9654,"depth":196,"text":9655},{"id":9677,"depth":196,"text":9678},{"id":9695,"depth":191,"text":9696,"children":9762},[9763,9764],{"id":5967,"depth":196,"text":5968},{"id":5957,"depth":196,"text":5958},{"id":9713,"depth":191,"text":9714},{"id":9732,"depth":191,"text":9733},"2024-07-04","Discover the new features in FlowFuse 2.6, and it's focus on improving the Node-RED development experience.","\u002Fblog\u002F2024\u002F07\u002Fimages\u002Frelease-2-6-july-2024.png",{"excerpt":9771},{"type":12,"value":9772},[9773],[15,9774,9473,9775,1423,9778,9484,9781,474],{},[22,9776,9478],{"href":9476,"rel":9777},[445],[22,9779,9483],{"href":9481,"rel":9780},[445],[22,9782,9489],{"href":9487,"rel":9783},[445],"\u002Fblog\u002F2024\u002F07\u002Fflowfuse-2-6-release",{"title":9466,"description":9768},{"loc":9784},"blog\u002F2024\u002F07\u002Fflowfuse-2-6-release","Lowering the barrier to entry for new users, and enhancing the flexibility and functionality of the platform.",[8441,223,530,9790,9791,224,9792,9793,438],"storage","editor","assistant","blueprint","mLO1Jie_w2zC0RIB9yuK01_51A0Gtd_6rpEeQHWf-iM",{"id":9796,"title":9797,"authors":9798,"body":9800,"cta":3,"date":10032,"description":10033,"extension":207,"image":10034,"lastUpdated":3,"meta":10035,"navigation":216,"path":10048,"seo":10049,"sitemap":10050,"stem":10051,"subtitle":10052,"tags":10053,"tldr":3,"video":3,"__hash__":10054},"blog\u002Fblog\u002F2024\u002F01\u002Frevolutionizing-manufacturing-impact-ai-chatgpt-technologies.md","AI and ChatGPT - Revolutionizing the Manufacturing Industry",[9799],"flowfuseteam",{"type":12,"value":9801,"toc":10018},[9802,9824,9828,9831,9835,9838,9842,9845,9849,9852,9856,9859,9863,9866,9870,9874,9891,9895,9912,9916,9928,9938,9948,9958,9968,9978,9988,9998,10008],[15,9803,9804,9805,9809,9810,9814,9815,9818,9819,9823],{},"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, ",[22,9806,9808],{"href":9807},"\u002Fblog\u002F2023\u002F12\u002Fai-use-cases\u002F","\"AI Use Cases that are shaping the next manufacturing frontier\"",", offers an insightful overview of AI's role in diverse areas. Following this, ",[22,9811,9813],{"href":9812},"\u002Fblog\u002F2023\u002F11\u002Fai-assistant\u002F","\"ChatGPT AI Assistants with Node-RED\""," examines the specific impact of AI assistants. The third article, ",[22,9816,9817],{"href":9419},"\"Node-RED Builder a ChatGPT GPT\"",", discusses the capabilities of generative pre-trained transformers like ChatGPT. Lastly, ",[22,9820,9822],{"href":9821},"\u002Fblog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers\u002F","\"How ChatGPT improves Node-RED Developer Experience\""," explores ChatGPT's application in Node-RED development, an important aspect for many in manufacturing.",[39,9825,9827],{"id":9826},"how-ai-and-chatgpt-are-impacting-manufacturing","How AI and ChatGPT are Impacting Manufacturing",[15,9829,9830],{},"AI and ChatGPT's integration into manufacturing indicates a shift in production process management. Here are some key impacts:",[143,9832,9834],{"id":9833},"boosting-efficiency-and-productivity","Boosting Efficiency and Productivity",[15,9836,9837],{},"AI, especially ChatGPT, enhances manufacturing efficiency by analyzing data to improve production lines, predict maintenance, and aid in design and development.",[143,9839,9841],{"id":9840},"automating-routine-tasks","Automating Routine Tasks",[15,9843,9844],{},"AI is adept at handling repetitive tasks, speeding up manufacturing and allowing human workers to engage in more complex production aspects.",[143,9846,9848],{"id":9847},"elevating-quality-control","Elevating Quality Control",[15,9850,9851],{},"AI algorithms consistently ensure high-quality standards, quickly identifying and fixing product defects or deviations.",[143,9853,9855],{"id":9854},"enabling-customization-and-flexibility","Enabling Customization and Flexibility",[15,9857,9858],{},"AI's learning and adaptability make it suitable for customizing production, allowing manufacturers to meet specific customer demands more efficiently.",[143,9860,9862],{"id":9861},"transforming-the-workforce","Transforming the Workforce",[15,9864,9865],{},"AI in manufacturing necessitates skilled workers to operate and maintain these systems, altering job roles and responsibilities.",[39,9867,9869],{"id":9868},"pros-and-cons-of-ai-and-chatgpt-in-manufacturing","Pros and Cons of AI and ChatGPT in Manufacturing",[143,9871,9873],{"id":9872},"pros","Pros",[3084,9875,9876,9879,9882,9885,9888],{},[50,9877,9878],{},"Boosted Productivity: AI-driven automation increases production efficiency.",[50,9880,9881],{},"Consistent Quality Assurance: AI ensures ongoing product quality.",[50,9883,9884],{},"Reduced Costs: AI optimizes resource use and minimizes waste.",[50,9886,9887],{},"Encouraging Innovation: AI facilitates new manufacturing methods and products.",[50,9889,9890],{},"Enhancing Safety: AI reduces human exposure to hazardous manufacturing conditions.",[143,9892,9894],{"id":9893},"cons","Cons",[3084,9896,9897,9900,9903,9906,9909],{},[50,9898,9899],{},"Initial Investment Costs: AI technology implementation can be expensive.",[50,9901,9902],{},"Need for Skilled Labor: Demand for workers proficient in AI technologies is growing.",[50,9904,9905],{},"Job Role Changes: Automation might decrease the need for certain labor roles.",[50,9907,9908],{},"Security Concerns: AI systems can be susceptible to cyber threats.",[50,9910,9911],{},"Technological Dependence: Excessive reliance on AI could limit problem-solving abilities in workers.",[39,9913,9915],{"id":9914},"frequently-asked-questions-faqs","Frequently Asked Questions (FAQs)",[9917,9918,9919,9923,9925],"details",{},[9920,9921,9922],"summary",{},"1. How is AI changing manufacturing?",[8595,9924],{},[53,9926,9927],{},"AI is altering manufacturing through automation, optimizing efficiency, and fostering production innovations.",[9917,9929,9930,9933,9935],{},[9920,9931,9932],{},"2. What is ChatGPT's role in manufacturing?",[8595,9934],{},[53,9936,9937],{},"ChatGPT aids in data analysis, automates processes, and improves communication and documentation in manufacturing.",[9917,9939,9940,9943,9945],{},[9920,9941,9942],{},"3. Are manufacturing jobs at risk due to AI?",[8595,9944],{},[53,9946,9947],{},"While AI may automate some repetitive jobs, it also creates opportunities for skilled labor in technology management and development.",[9917,9949,9950,9953,9955],{},[9920,9951,9952],{},"4. Can AI enhance manufacturing product quality?",[8595,9954],{},[53,9956,9957],{},"Yes, AI's continuous monitoring and analysis significantly boost quality control.",[9917,9959,9960,9963,9965],{},[9920,9961,9962],{},"5. What are the main challenges of integrating AI in manufacturing?",[8595,9964],{},[53,9966,9967],{},"Challenges include high implementation costs, the need for skilled labor, and transitioning to automated processes.",[9917,9969,9970,9973,9975],{},[9920,9971,9972],{},"6. Is AI cost-effective in manufacturing?",[8595,9974],{},[53,9976,9977],{},"Despite high initial costs, AI can lead to long-term savings through improved efficiency and waste reduction.",[9917,9979,9980,9983,9985],{},[9920,9981,9982],{},"7. How does AI affect manufacturing worker safety?",[8595,9984],{},[53,9986,9987],{},"AI reduces risk by taking over hazardous tasks, improving overall workplace safety.",[9917,9989,9990,9993,9995],{},[9920,9991,9992],{},"8. Can small manufacturers benefit from AI?",[8595,9994],{},[53,9996,9997],{},"Yes, AI solutions are increasingly accessible for small-scale manufacturers.",[9917,9999,10000,10003,10005],{},[9920,10001,10002],{},"9. What training is required for AI-enabled manufacturing workers?",[8595,10004],{},[53,10006,10007],{},"Training in AI system operation, data analysis, and potentially programming skills is needed.",[9917,10009,10010,10013,10015],{},[9920,10011,10012],{},"10. What does the future hold for AI in manufacturing?",[8595,10014],{},[53,10016,10017],{},"The future suggests more integrated, intelligent, and adaptable manufacturing processes driven by AI advancements.",{"title":187,"searchDepth":188,"depth":188,"links":10019},[10020,10027,10031],{"id":9826,"depth":191,"text":9827,"children":10021},[10022,10023,10024,10025,10026],{"id":9833,"depth":196,"text":9834},{"id":9840,"depth":196,"text":9841},{"id":9847,"depth":196,"text":9848},{"id":9854,"depth":196,"text":9855},{"id":9861,"depth":196,"text":9862},{"id":9868,"depth":191,"text":9869,"children":10028},[10029,10030],{"id":9872,"depth":196,"text":9873},{"id":9893,"depth":196,"text":9894},{"id":9914,"depth":191,"text":9915},"2024-01-31","Explore how AI and ChatGPT revolutionize manufacturing with boosted efficiency, quality control, and workforce transformation.","\u002Fblog\u002F2024\u002F01\u002Fimages\u002FFuturistic factory with robots.png",{"excerpt":10036},{"type":12,"value":10037},[10038],[15,10039,9804,10040,9809,10042,9814,10044,9818,10046,9823],{},[22,10041,9808],{"href":9807},[22,10043,9813],{"href":9812},[22,10045,9817],{"href":9419},[22,10047,9822],{"href":9821},"\u002Fblog\u002F2024\u002F01\u002Frevolutionizing-manufacturing-impact-ai-chatgpt-technologies",{"title":9797,"description":10033},{"loc":10048},"blog\u002F2024\u002F01\u002Frevolutionizing-manufacturing-impact-ai-chatgpt-technologies","How AI and Conversational Technologies are Transforming Industrial Processes",[8441,223,224],"NtTuY1ugVAqdG2WsKOPo0W3Ytk9Yj_pREKSjyAQK3z4",{"id":10056,"title":10057,"authors":10058,"body":10059,"cta":3,"date":11623,"description":11624,"extension":207,"image":11602,"lastUpdated":3,"meta":11625,"navigation":216,"path":11632,"seo":11633,"sitemap":11634,"stem":11635,"subtitle":11636,"tags":11637,"tldr":3,"video":3,"__hash__":11640},"blog\u002Fblog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red.md","Speech-Driven Chatbot System with Node-RED",[10],{"type":12,"value":10060,"toc":11612},[10061,10064,10067,10071,10074,10094,10098,10106,10110,10119,10127,10130,10133,10136,10316,10319,10322,11012,11016,11023,11035,11042,11046,11049,11060,11067,11071,11074,11089,11093,11096,11107,11110,11112,11169,11171,11174,11570,11573,11580,11584,11591,11597,11604,11606,11609],[15,10062,10063],{},"Have you ever wanted to integrate speech recognition and synthesis into your Node-RED project and thought it was too complex? Often it has required external services or APIs. However, in this guide, we show you how you can use speech recognition and synthesis in your Node-RED projects without needing an external service or API.",[15,10065,10066],{},"In addition, we make things more interesting by building a system that can listen to us and respond like humans using the Chat-GPT API.\nLet's get started!",[39,10068,10070],{"id":10069},"what-exactly-is-speech-recognition-and-synthesis","What exactly is speech recognition and synthesis?",[15,10072,10073],{},"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.",[47,10075,10076,10082,10088],{},[50,10077,10078,10081],{},[53,10079,10080],{},"Hands-Free Operation:"," Using speech recognition technology is often used today to perform tasks such as making calls, sending messages, or controlling smart home devices without the need for physical interaction.",[50,10083,10084,10087],{},[53,10085,10086],{},"Accessibility:"," It allows individuals with visual impairments to access digital content through spoken words and as discussed above, to control devices without physical interaction.",[50,10089,10090,10093],{},[53,10091,10092],{},"Efficient Content Consumption:"," It allows us to listen to information instead of reading. For example, in the audiobook industry by using speech synthesis technology they create audio versions of books which helps users to be more productive.",[39,10095,10097],{"id":10096},"installing-dashboard-20","Installing Dashboard 2.0",[15,10099,10100,10101,10105],{},"Install Dashboard 2.0. Follow these ",[22,10102,10104],{"href":10103},"\u002Fblog\u002F2024\u002F03\u002Fdashboard-getting-started\u002F","instructions"," to get up and running.",[39,10107,10109],{"id":10108},"building-speech-to-text-vue-component","Building Speech-to-Text Vue component",[15,10111,10112,10113,10118],{},"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 ",[22,10114,10117],{"href":10115,"rel":10116},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FAPI\u002FWeb_Speech_API",[445],"Web Speech API"," is not an external API.\nThis will process your speech locally as it is a JavaScript API that allows us to use speech-related functionalities, such as speech recognition and synthesis, in a web browser directly. It is widely present in modern browsers (except Firefox) which eliminates the need for external APIs to implement these features. Let's now start to build that component.",[3084,10120,10121,10124],{},[50,10122,10123],{},"Drag a ui template widget to canvas and select the created group.",[50,10125,10126],{},"Paste the below Vue snippets into the template widget step by step.",[15,10128,10129],{},"If you are unfamiliar with Vue, we have added comments that will help you to understand the code better.",[15,10131,10132],{},"We are going to start by pasting a user interface’s snippet which will allow us to interact with our system. This snippet adds a button that triggers our system to listen, an Icon, and a paragraph to display speech recognition results on the dashboard.",[15,10134,10135],{},"{% raw %}",[1195,10137,10141],{"className":10138,"code":10139,"language":10140,"meta":187,"style":187},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Ctemplate>\n  \u003Cdiv style=\"display: flex; flex-direction: column; justify-content: center; align-items: center;\">\n    \u003C!-- Button triggers recording when clicked -->\n    \u003Cbutton @click=\"startRecording\">\n      \u003C!-- Microphone icon inside the button -->\n      \u003Cimg alt=\"Microphone\" style=\"height: 62px; width: 62px\" :src=\"microphoneIcon\">\n    \u003C\u002Fbutton>\n    \u003C!-- Displaying speech recognition results -->\n    \u003Cp> \u003Cstrong>You:\u003C\u002Fstrong> {{ results }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","html",[76,10142,10143,10154,10175,10180,10202,10207,10251,10260,10265,10299,10308],{"__ignoreMap":187},[1238,10144,10145,10148,10151],{"class":1240,"line":1241},[1238,10146,10147],{"class":1244},"\u003C",[1238,10149,10150],{"class":3277},"template",[1238,10152,10153],{"class":1244},">\n",[1238,10155,10156,10159,10161,10164,10166,10168,10171,10173],{"class":1240,"line":191},[1238,10157,10158],{"class":1244},"  \u003C",[1238,10160,292],{"class":3277},[1238,10162,10163],{"class":1253}," style",[1238,10165,4230],{"class":1244},[1238,10167,1257],{"class":1244},[1238,10169,10170],{"class":1266},"display: flex; flex-direction: column; justify-content: center; align-items: center;",[1238,10172,1257],{"class":1244},[1238,10174,10153],{"class":1244},[1238,10176,10177],{"class":1240,"line":196},[1238,10178,10179],{"class":4041},"    \u003C!-- Button triggers recording when clicked -->\n",[1238,10181,10182,10185,10188,10191,10193,10195,10198,10200],{"class":1240,"line":188},[1238,10183,10184],{"class":1244},"    \u003C",[1238,10186,10187],{"class":3277},"button",[1238,10189,10190],{"class":1253}," @click",[1238,10192,4230],{"class":1244},[1238,10194,1257],{"class":1244},[1238,10196,10197],{"class":1266},"startRecording",[1238,10199,1257],{"class":1244},[1238,10201,10153],{"class":1244},[1238,10203,10204],{"class":1240,"line":1334},[1238,10205,10206],{"class":4041},"      \u003C!-- Microphone icon inside the button -->\n",[1238,10208,10209,10212,10214,10217,10219,10221,10224,10226,10228,10230,10232,10235,10237,10240,10242,10244,10247,10249],{"class":1240,"line":1372},[1238,10210,10211],{"class":1244},"      \u003C",[1238,10213,30],{"class":3277},[1238,10215,10216],{"class":1253}," alt",[1238,10218,4230],{"class":1244},[1238,10220,1257],{"class":1244},[1238,10222,10223],{"class":1266},"Microphone",[1238,10225,1257],{"class":1244},[1238,10227,10163],{"class":1253},[1238,10229,4230],{"class":1244},[1238,10231,1257],{"class":1244},[1238,10233,10234],{"class":1266},"height: 62px; width: 62px",[1238,10236,1257],{"class":1244},[1238,10238,10239],{"class":1253}," :src",[1238,10241,4230],{"class":1244},[1238,10243,1257],{"class":1244},[1238,10245,10246],{"class":1266},"microphoneIcon",[1238,10248,1257],{"class":1244},[1238,10250,10153],{"class":1244},[1238,10252,10253,10256,10258],{"class":1240,"line":1411},[1238,10254,10255],{"class":1244},"    \u003C\u002F",[1238,10257,10187],{"class":3277},[1238,10259,10153],{"class":1244},[1238,10261,10262],{"class":1240,"line":1807},[1238,10263,10264],{"class":4041},"    \u003C!-- Displaying speech recognition results -->\n",[1238,10266,10267,10269,10271,10273,10276,10278,10280,10283,10286,10288,10290,10293,10295,10297],{"class":1240,"line":1813},[1238,10268,10184],{"class":1244},[1238,10270,15],{"class":3277},[1238,10272,5070],{"class":1244},[1238,10274,10275],{"class":1244}," \u003C",[1238,10277,53],{"class":3277},[1238,10279,5070],{"class":1244},[1238,10281,10282],{"class":1327},"You:",[1238,10284,10285],{"class":1244},"\u003C\u002F",[1238,10287,53],{"class":3277},[1238,10289,5070],{"class":1244},[1238,10291,10292],{"class":1327}," {{ results }}",[1238,10294,10285],{"class":1244},[1238,10296,15],{"class":3277},[1238,10298,10153],{"class":1244},[1238,10300,10301,10304,10306],{"class":1240,"line":1819},[1238,10302,10303],{"class":1244},"  \u003C\u002F",[1238,10305,292],{"class":3277},[1238,10307,10153],{"class":1244},[1238,10309,10310,10312,10314],{"class":1240,"line":1825},[1238,10311,10285],{"class":1244},[1238,10313,10150],{"class":3277},[1238,10315,10153],{"class":1244},[15,10317,10318],{},"{% endraw %}",[15,10320,10321],{},"Now paste the below script right after the HTML in the template widget, This script adds functionality of speech recognition in our system.",[1195,10323,10325],{"className":10138,"code":10324,"language":10140,"meta":187,"style":187},"\u003Cscript>\nexport default {\n data() {\n   return {\n     \u002F\u002F Initial data properties\n     buttonText: 'Speak',\n     microphoneIcon: 'http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png',\n     recognition: null,\n     results: '',\n   };\n },\n methods: {\n   \u002F\u002F Method to start recording\n   startRecording() {\n     this.buttonText = 'Recording';\n     this.recognition.stop();\n     this.recognition.start();\n   },\n   \u002F\u002F Method to process the speech recognition results\n   processSpeech(event) {\n     let results = Array.from(event.results).map(result =>   result[0].transcript).join('');\n     this.results = results;\n     this.$emit('speak', results);\n     \u002F\u002FSending result to next node as payload\n     this.send(results);\n   },\n   \u002F\u002F Method to handle the start of recognition\n   handleRecognitionStart() {\n     this.microphoneIcon = 'https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002F0\u002F06\u002FMic-Animation.gif';\n   },\n   \u002F\u002F Method to handle recognition errors\n   handleRecognitionError(event) {\n     this.microphoneIcon = event.error === 'no-speech' || event.error === 'audio-capture'\n       ? 'https:\u002F\u002Fi.ytimg.com\u002Fvi\u002F9YQU797Oy0Y\u002Fhqdefault.jpg'\n       : this.microphoneIcon;\n   },\n   \u002F\u002F Method to handle the end of recognition\n   handleRecognitionEnd() {\n     this.microphoneIcon = 'http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png';\n   },\n   \u002F\u002F Method to set up the SpeechRecognition object\n   setupRecognition() {\n     this.recognition = new webkitSpeechRecognition();\n     this.recognition.continuous = false;\n     this.recognition.interimResults = false;\n     this.recognition.onresult = this.processSpeech;\n     this.recognition.onstart = this.handleRecognitionStart;\n     this.recognition.onerror = this.handleRecognitionError;\n     this.recognition.onend = this.handleRecognitionEnd;\n   },\n },\n mounted() {\n   \u002F\u002F Initialize SpeechRecognition when the component is mounted\n   this.setupRecognition();\n },\n};\n\u003C\u002Fscript>\n",[76,10326,10327,10336,10346,10355,10362,10367,10383,10399,10409,10421,10426,10431,10440,10445,10454,10473,10489,10504,10509,10514,10528,10596,10608,10632,10637,10652,10656,10661,10670,10687,10691,10696,10709,10754,10766,10778,10782,10787,10796,10812,10816,10821,10830,10847,10865,10882,10902,10922,10942,10962,10966,10970,10979,10984,10996,11000,11004],{"__ignoreMap":187},[1238,10328,10329,10331,10334],{"class":1240,"line":1241},[1238,10330,10147],{"class":1244},[1238,10332,10333],{"class":3277},"script",[1238,10335,10153],{"class":1244},[1238,10337,10338,10341,10344],{"class":1240,"line":191},[1238,10339,10340],{"class":3672},"export",[1238,10342,10343],{"class":3672}," default",[1238,10345,3255],{"class":1244},[1238,10347,10348,10351,10353],{"class":1240,"line":196},[1238,10349,10350],{"class":3277}," data",[1238,10352,5084],{"class":1244},[1238,10354,3255],{"class":1244},[1238,10356,10357,10360],{"class":1240,"line":188},[1238,10358,10359],{"class":3672},"   return",[1238,10361,3255],{"class":1244},[1238,10363,10364],{"class":1240,"line":1334},[1238,10365,10366],{"class":4041},"     \u002F\u002F Initial data properties\n",[1238,10368,10369,10372,10374,10376,10379,10381],{"class":1240,"line":1372},[1238,10370,10371],{"class":3277},"     buttonText",[1238,10373,1260],{"class":1244},[1238,10375,5234],{"class":1244},[1238,10377,10378],{"class":1266},"Speak",[1238,10380,4070],{"class":1244},[1238,10382,1272],{"class":1244},[1238,10384,10385,10388,10390,10392,10395,10397],{"class":1240,"line":1411},[1238,10386,10387],{"class":3277},"     microphoneIcon",[1238,10389,1260],{"class":1244},[1238,10391,5234],{"class":1244},[1238,10393,10394],{"class":1266},"http:\u002F\u002Ficons.iconarchive.com\u002Ficons\u002Fblackvariant\u002Fbutton-ui-requests-15\u002F512\u002FMicrophone-icon.png",[1238,10396,4070],{"class":1244},[1238,10398,1272],{"class":1244},[1238,10400,10401,10404,10406],{"class":1240,"line":1807},[1238,10402,10403],{"class":3277},"     recognition",[1238,10405,1260],{"class":1244},[1238,10407,10408],{"class":1244}," null,\n",[1238,10410,10411,10414,10416,10419],{"class":1240,"line":1813},[1238,10412,10413],{"class":3277},"     results",[1238,10415,1260],{"class":1244},[1238,10417,10418],{"class":1244}," ''",[1238,10420,1272],{"class":1244},[1238,10422,10423],{"class":1240,"line":1819},[1238,10424,10425],{"class":1244},"   };\n",[1238,10427,10428],{"class":1240,"line":1825},[1238,10429,10430],{"class":1244}," },\n",[1238,10432,10433,10436,10438],{"class":1240,"line":1831},[1238,10434,10435],{"class":3277}," methods",[1238,10437,1260],{"class":1244},[1238,10439,3255],{"class":1244},[1238,10441,10442],{"class":1240,"line":1837},[1238,10443,10444],{"class":4041},"   \u002F\u002F Method to start recording\n",[1238,10446,10447,10450,10452],{"class":1240,"line":1843},[1238,10448,10449],{"class":3277},"   startRecording",[1238,10451,5084],{"class":1244},[1238,10453,3255],{"class":1244},[1238,10455,10456,10459,10462,10464,10466,10469,10471],{"class":1240,"line":1849},[1238,10457,10458],{"class":1244},"     this.",[1238,10460,10461],{"class":1327},"buttonText",[1238,10463,3266],{"class":1244},[1238,10465,5234],{"class":1244},[1238,10467,10468],{"class":1266},"Recording",[1238,10470,4070],{"class":1244},[1238,10472,3285],{"class":1244},[1238,10474,10475,10477,10480,10482,10485,10487],{"class":1240,"line":1855},[1238,10476,10458],{"class":1244},[1238,10478,10479],{"class":1327},"recognition",[1238,10481,474],{"class":1244},[1238,10483,10484],{"class":1512},"stop",[1238,10486,5084],{"class":3277},[1238,10488,3285],{"class":1244},[1238,10490,10491,10493,10495,10497,10500,10502],{"class":1240,"line":1860},[1238,10492,10458],{"class":1244},[1238,10494,10479],{"class":1327},[1238,10496,474],{"class":1244},[1238,10498,10499],{"class":1512},"start",[1238,10501,5084],{"class":3277},[1238,10503,3285],{"class":1244},[1238,10505,10506],{"class":1240,"line":1866},[1238,10507,10508],{"class":1244},"   },\n",[1238,10510,10511],{"class":1240,"line":1872},[1238,10512,10513],{"class":4041},"   \u002F\u002F Method to process the speech recognition results\n",[1238,10515,10516,10519,10521,10524,10526],{"class":1240,"line":1878},[1238,10517,10518],{"class":3277},"   processSpeech",[1238,10520,3245],{"class":1244},[1238,10522,10523],{"class":3248},"event",[1238,10525,3252],{"class":1244},[1238,10527,3255],{"class":1244},[1238,10529,10530,10533,10536,10538,10540,10542,10544,10546,10548,10550,10553,10555,10557,10559,10561,10564,10566,10569,10571,10573,10575,10577,10580,10582,10584,10587,10589,10592,10594],{"class":1240,"line":1884},[1238,10531,10532],{"class":1253},"     let",[1238,10534,10535],{"class":1327}," results",[1238,10537,3266],{"class":1244},[1238,10539,4895],{"class":1327},[1238,10541,474],{"class":1244},[1238,10543,4900],{"class":1512},[1238,10545,3245],{"class":3277},[1238,10547,10523],{"class":1327},[1238,10549,474],{"class":1244},[1238,10551,10552],{"class":1327},"results",[1238,10554,3252],{"class":3277},[1238,10556,474],{"class":1244},[1238,10558,3274],{"class":1512},[1238,10560,3245],{"class":3277},[1238,10562,10563],{"class":3248},"result",[1238,10565,3335],{"class":1253},[1238,10567,10568],{"class":1327},"   result",[1238,10570,4434],{"class":3277},[1238,10572,4852],{"class":1286},[1238,10574,4367],{"class":3277},[1238,10576,474],{"class":1244},[1238,10578,10579],{"class":1327},"transcript",[1238,10581,3252],{"class":3277},[1238,10583,474],{"class":1244},[1238,10585,10586],{"class":1512},"join",[1238,10588,3245],{"class":3277},[1238,10590,10591],{"class":1244},"''",[1238,10593,3252],{"class":3277},[1238,10595,3285],{"class":1244},[1238,10597,10598,10600,10602,10604,10606],{"class":1240,"line":1890},[1238,10599,10458],{"class":1244},[1238,10601,10552],{"class":1327},[1238,10603,3266],{"class":1244},[1238,10605,10535],{"class":1327},[1238,10607,3285],{"class":1244},[1238,10609,10610,10612,10615,10617,10619,10622,10624,10626,10628,10630],{"class":1240,"line":1896},[1238,10611,10458],{"class":1244},[1238,10613,10614],{"class":1512},"$emit",[1238,10616,3245],{"class":3277},[1238,10618,4070],{"class":1244},[1238,10620,10621],{"class":1266},"speak",[1238,10623,4070],{"class":1244},[1238,10625,1309],{"class":1244},[1238,10627,10535],{"class":1327},[1238,10629,3252],{"class":3277},[1238,10631,3285],{"class":1244},[1238,10633,10634],{"class":1240,"line":1902},[1238,10635,10636],{"class":4041},"     \u002F\u002FSending result to next node as payload\n",[1238,10638,10639,10641,10644,10646,10648,10650],{"class":1240,"line":1907},[1238,10640,10458],{"class":1244},[1238,10642,10643],{"class":1512},"send",[1238,10645,3245],{"class":3277},[1238,10647,10552],{"class":1327},[1238,10649,3252],{"class":3277},[1238,10651,3285],{"class":1244},[1238,10653,10654],{"class":1240,"line":1913},[1238,10655,10508],{"class":1244},[1238,10657,10658],{"class":1240,"line":1919},[1238,10659,10660],{"class":4041},"   \u002F\u002F Method to handle the start of recognition\n",[1238,10662,10663,10666,10668],{"class":1240,"line":1925},[1238,10664,10665],{"class":3277},"   handleRecognitionStart",[1238,10667,5084],{"class":1244},[1238,10669,3255],{"class":1244},[1238,10671,10672,10674,10676,10678,10680,10683,10685],{"class":1240,"line":1931},[1238,10673,10458],{"class":1244},[1238,10675,10246],{"class":1327},[1238,10677,3266],{"class":1244},[1238,10679,5234],{"class":1244},[1238,10681,10682],{"class":1266},"https:\u002F\u002Fupload.wikimedia.org\u002Fwikipedia\u002Fcommons\u002F0\u002F06\u002FMic-Animation.gif",[1238,10684,4070],{"class":1244},[1238,10686,3285],{"class":1244},[1238,10688,10689],{"class":1240,"line":1937},[1238,10690,10508],{"class":1244},[1238,10692,10693],{"class":1240,"line":1943},[1238,10694,10695],{"class":4041},"   \u002F\u002F Method to handle recognition errors\n",[1238,10697,10698,10701,10703,10705,10707],{"class":1240,"line":1949},[1238,10699,10700],{"class":3277},"   handleRecognitionError",[1238,10702,3245],{"class":1244},[1238,10704,10523],{"class":3248},[1238,10706,3252],{"class":1244},[1238,10708,3255],{"class":1244},[1238,10710,10711,10713,10715,10717,10720,10722,10725,10728,10730,10733,10735,10738,10740,10742,10744,10746,10748,10751],{"class":1240,"line":1955},[1238,10712,10458],{"class":1244},[1238,10714,10246],{"class":1327},[1238,10716,3266],{"class":1244},[1238,10718,10719],{"class":1327}," event",[1238,10721,474],{"class":1244},[1238,10723,10724],{"class":1327},"error",[1238,10726,10727],{"class":1244}," ===",[1238,10729,5234],{"class":1244},[1238,10731,10732],{"class":1266},"no-speech",[1238,10734,4070],{"class":1244},[1238,10736,10737],{"class":1244}," ||",[1238,10739,10719],{"class":1327},[1238,10741,474],{"class":1244},[1238,10743,10724],{"class":1327},[1238,10745,10727],{"class":1244},[1238,10747,5234],{"class":1244},[1238,10749,10750],{"class":1266},"audio-capture",[1238,10752,10753],{"class":1244},"'\n",[1238,10755,10756,10759,10761,10764],{"class":1240,"line":1960},[1238,10757,10758],{"class":1244},"       ?",[1238,10760,5234],{"class":1244},[1238,10762,10763],{"class":1266},"https:\u002F\u002Fi.ytimg.com\u002Fvi\u002F9YQU797Oy0Y\u002Fhqdefault.jpg",[1238,10765,10753],{"class":1244},[1238,10767,10768,10771,10774,10776],{"class":1240,"line":1966},[1238,10769,10770],{"class":1244},"       :",[1238,10772,10773],{"class":1244}," this.",[1238,10775,10246],{"class":1327},[1238,10777,3285],{"class":1244},[1238,10779,10780],{"class":1240,"line":1972},[1238,10781,10508],{"class":1244},[1238,10783,10784],{"class":1240,"line":1977},[1238,10785,10786],{"class":4041},"   \u002F\u002F Method to handle the end of recognition\n",[1238,10788,10789,10792,10794],{"class":1240,"line":1983},[1238,10790,10791],{"class":3277},"   handleRecognitionEnd",[1238,10793,5084],{"class":1244},[1238,10795,3255],{"class":1244},[1238,10797,10798,10800,10802,10804,10806,10808,10810],{"class":1240,"line":1989},[1238,10799,10458],{"class":1244},[1238,10801,10246],{"class":1327},[1238,10803,3266],{"class":1244},[1238,10805,5234],{"class":1244},[1238,10807,10394],{"class":1266},[1238,10809,4070],{"class":1244},[1238,10811,3285],{"class":1244},[1238,10813,10814],{"class":1240,"line":1995},[1238,10815,10508],{"class":1244},[1238,10817,10818],{"class":1240,"line":2001},[1238,10819,10820],{"class":4041},"   \u002F\u002F Method to set up the SpeechRecognition object\n",[1238,10822,10823,10826,10828],{"class":1240,"line":2007},[1238,10824,10825],{"class":3277},"   setupRecognition",[1238,10827,5084],{"class":1244},[1238,10829,3255],{"class":1244},[1238,10831,10832,10834,10836,10838,10840,10843,10845],{"class":1240,"line":2012},[1238,10833,10458],{"class":1244},[1238,10835,10479],{"class":1327},[1238,10837,3266],{"class":1244},[1238,10839,4554],{"class":1244},[1238,10841,10842],{"class":1512}," webkitSpeechRecognition",[1238,10844,5084],{"class":3277},[1238,10846,3285],{"class":1244},[1238,10848,10849,10851,10853,10855,10858,10860,10863],{"class":1240,"line":2018},[1238,10850,10458],{"class":1244},[1238,10852,10479],{"class":1327},[1238,10854,474],{"class":1244},[1238,10856,10857],{"class":1327},"continuous",[1238,10859,3266],{"class":1244},[1238,10861,10862],{"class":4807}," false",[1238,10864,3285],{"class":1244},[1238,10866,10867,10869,10871,10873,10876,10878,10880],{"class":1240,"line":2024},[1238,10868,10458],{"class":1244},[1238,10870,10479],{"class":1327},[1238,10872,474],{"class":1244},[1238,10874,10875],{"class":1327},"interimResults",[1238,10877,3266],{"class":1244},[1238,10879,10862],{"class":4807},[1238,10881,3285],{"class":1244},[1238,10883,10884,10886,10888,10890,10893,10895,10897,10900],{"class":1240,"line":2030},[1238,10885,10458],{"class":1244},[1238,10887,10479],{"class":1327},[1238,10889,474],{"class":1244},[1238,10891,10892],{"class":1327},"onresult",[1238,10894,3266],{"class":1244},[1238,10896,10773],{"class":1244},[1238,10898,10899],{"class":1327},"processSpeech",[1238,10901,3285],{"class":1244},[1238,10903,10904,10906,10908,10910,10913,10915,10917,10920],{"class":1240,"line":2036},[1238,10905,10458],{"class":1244},[1238,10907,10479],{"class":1327},[1238,10909,474],{"class":1244},[1238,10911,10912],{"class":1327},"onstart",[1238,10914,3266],{"class":1244},[1238,10916,10773],{"class":1244},[1238,10918,10919],{"class":1327},"handleRecognitionStart",[1238,10921,3285],{"class":1244},[1238,10923,10924,10926,10928,10930,10933,10935,10937,10940],{"class":1240,"line":2042},[1238,10925,10458],{"class":1244},[1238,10927,10479],{"class":1327},[1238,10929,474],{"class":1244},[1238,10931,10932],{"class":1327},"onerror",[1238,10934,3266],{"class":1244},[1238,10936,10773],{"class":1244},[1238,10938,10939],{"class":1327},"handleRecognitionError",[1238,10941,3285],{"class":1244},[1238,10943,10944,10946,10948,10950,10953,10955,10957,10960],{"class":1240,"line":2048},[1238,10945,10458],{"class":1244},[1238,10947,10479],{"class":1327},[1238,10949,474],{"class":1244},[1238,10951,10952],{"class":1327},"onend",[1238,10954,3266],{"class":1244},[1238,10956,10773],{"class":1244},[1238,10958,10959],{"class":1327},"handleRecognitionEnd",[1238,10961,3285],{"class":1244},[1238,10963,10964],{"class":1240,"line":2054},[1238,10965,10508],{"class":1244},[1238,10967,10968],{"class":1240,"line":2060},[1238,10969,10430],{"class":1244},[1238,10971,10972,10975,10977],{"class":1240,"line":2066},[1238,10973,10974],{"class":3277}," mounted",[1238,10976,5084],{"class":1244},[1238,10978,3255],{"class":1244},[1238,10980,10981],{"class":1240,"line":2072},[1238,10982,10983],{"class":4041},"   \u002F\u002F Initialize SpeechRecognition when the component is mounted\n",[1238,10985,10986,10989,10992,10994],{"class":1240,"line":2078},[1238,10987,10988],{"class":1244},"   this.",[1238,10990,10991],{"class":1512},"setupRecognition",[1238,10993,5084],{"class":3277},[1238,10995,3285],{"class":1244},[1238,10997,10998],{"class":1240,"line":2084},[1238,10999,10430],{"class":1244},[1238,11001,11002],{"class":1240,"line":2090},[1238,11003,4602],{"class":1244},[1238,11005,11006,11008,11010],{"class":1240,"line":2095},[1238,11007,10285],{"class":1244},[1238,11009,10333],{"class":3277},[1238,11011,10153],{"class":1244},[39,11013,11015],{"id":11014},"adding-an-environment-variable","Adding an Environment variable",[15,11017,11018,11019],{},"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 ",[22,11020,11022],{"href":11021},"\u002Fblog\u002F2023\u002F01\u002Fenvironment-variables-in-node-red\u002F","Using Environment Variables in Node-RED",[3084,11024,11025,11028],{},[50,11026,11027],{},"Navigate to the instance's setting and then go to the environment section.",[50,11029,11030,11031,11034],{},"Click on the ",[76,11032,11033],{},"add variable"," button and add a variable for Chat-gpt API.",[15,11036,11037],{},[30,11038],{"alt":11039,"src":11040,"title":11041},"\"Setting environment variable for Chat-gpt token\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-environment-section.png","Setting environment variable for chat-gpt token",[39,11043,11045],{"id":11044},"setting-msg-property","Setting msg property",[15,11047,11048],{},"Now let’s set that added environment variables as msg's property.",[3084,11050,11051,11054,11057],{},[50,11052,11053],{},"Add a change node to canvas.",[50,11055,11056],{},"Set environment variable to ms.token property.",[50,11058,11059],{},"Connect the change node’s input to the template widget’s output.",[15,11061,11062],{},[30,11063],{"alt":11064,"src":11065,"title":11066},"\"Setting msg's property for Chat-gpt token\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-change-node.png","Setting msg's property for Chat-gpt token",[39,11068,11070],{"id":11069},"installing-and-configuring-custom-node","Installing and configuring custom node",[15,11072,11073],{},"In this section, we will install a custom node that will allow us to interact with the Chat-gpt AI model.",[3084,11075,11076,11083,11086],{},[50,11077,11078,11079,11082],{},"Install ",[76,11080,11081],{},"@sumit_shinde_84\u002Fnode-red-contrib-node-gpt"," by pallet manager, you can use other nodes according to your preference.",[50,11084,11085],{},"Drag a ChatGPT node to canvas.",[50,11087,11088],{},"Connect the ChatGPT node’s input to the change node’s output.",[39,11090,11092],{"id":11091},"building-text-to-speech-vue-component","Building Text-to-Speech Vue component",[15,11094,11095],{},"We will build a Vue component that converts text received from ChatGPT into speech.",[3084,11097,11098,11101,11104],{},[50,11099,11100],{},"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.",[50,11102,11103],{},"Paste the below Vue snippets into the template widget.",[50,11105,11106],{},"Connect the template widget’s input to the ChatGPT node’s output.",[15,11108,11109],{},"Paste the below snippet in the template widget which displays chat-gpt response on the dashboard",[15,11111,10135],{},[1195,11113,11115],{"className":10138,"code":11114,"language":10140,"meta":187,"style":187},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cstrong> Chat-gpt: \u003C\u002Fstrong> {{textToSpeech}}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[76,11116,11117,11125,11133,11153,11161],{"__ignoreMap":187},[1238,11118,11119,11121,11123],{"class":1240,"line":1241},[1238,11120,10147],{"class":1244},[1238,11122,10150],{"class":3277},[1238,11124,10153],{"class":1244},[1238,11126,11127,11129,11131],{"class":1240,"line":191},[1238,11128,10158],{"class":1244},[1238,11130,292],{"class":3277},[1238,11132,10153],{"class":1244},[1238,11134,11135,11137,11139,11141,11144,11146,11148,11150],{"class":1240,"line":196},[1238,11136,10184],{"class":1244},[1238,11138,53],{"class":3277},[1238,11140,5070],{"class":1244},[1238,11142,11143],{"class":1327}," Chat-gpt: ",[1238,11145,10285],{"class":1244},[1238,11147,53],{"class":3277},[1238,11149,5070],{"class":1244},[1238,11151,11152],{"class":1327}," {{textToSpeech}}\n",[1238,11154,11155,11157,11159],{"class":1240,"line":188},[1238,11156,10303],{"class":1244},[1238,11158,292],{"class":3277},[1238,11160,10153],{"class":1244},[1238,11162,11163,11165,11167],{"class":1240,"line":1334},[1238,11164,10285],{"class":1244},[1238,11166,10150],{"class":3277},[1238,11168,10153],{"class":1244},[15,11170,10318],{},[15,11172,11173],{},"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.",[1195,11175,11177],{"className":10138,"code":11176,"language":10140,"meta":187,"style":187},"\u003Cscript>\n  export default {\n  data() {\n    return {\n      \u002F\u002F Data property to store the text to be spoken\n      textToSpeech: '',\n    };\n  },\n  methods: {\n    \u002F\u002F Method to trigger text-to-speech\n    speakText() {\n      \u002F\u002F Check if there is non-empty text to speech\n      if (this.textToSpeech.trim() !== '') {\n        \u002F\u002F Create a SpeechSynthesisUtterance with the text to be spoken\n        const utterance = new  SpeechSynthesisUtterance(this.textToSpeech);\n        \u002F\u002F Use the SpeechSynthesis API to speak the provided text\n        window.speechSynthesis.speak(utterance);\n      }\n    },\n  },\n  mounted() {\n    \u002F\u002F Event listener for receiving messages    \n  \n     this.$socket.on('msg-input:' + this.id, (msg) => {\n\n      \u002F\u002F Update the textToSpeech property with the received message payload\n      this.textToSpeech = msg.payload;\n     \n      \u002F\u002F Trigger text-to-speech with the received message\n      this.speakText();\n    });\n  },\n};\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n  textarea {\n    width: 100%;\n    height: 100px;\n    margin-bottom: 10px;\n  }\n\u003C\u002Fstyle>\n",[76,11178,11179,11187,11196,11205,11211,11216,11227,11232,11237,11246,11251,11260,11265,11295,11300,11325,11330,11353,11358,11363,11367,11376,11381,11386,11426,11430,11435,11452,11457,11462,11473,11482,11486,11490,11498,11502,11513,11520,11533,11545,11557,11562],{"__ignoreMap":187},[1238,11180,11181,11183,11185],{"class":1240,"line":1241},[1238,11182,10147],{"class":1244},[1238,11184,10333],{"class":3277},[1238,11186,10153],{"class":1244},[1238,11188,11189,11192,11194],{"class":1240,"line":191},[1238,11190,11191],{"class":3672},"  export",[1238,11193,10343],{"class":3672},[1238,11195,3255],{"class":1244},[1238,11197,11198,11201,11203],{"class":1240,"line":196},[1238,11199,11200],{"class":3277},"  data",[1238,11202,5084],{"class":1244},[1238,11204,3255],{"class":1244},[1238,11206,11207,11209],{"class":1240,"line":188},[1238,11208,3673],{"class":3672},[1238,11210,3255],{"class":1244},[1238,11212,11213],{"class":1240,"line":1334},[1238,11214,11215],{"class":4041},"      \u002F\u002F Data property to store the text to be spoken\n",[1238,11217,11218,11221,11223,11225],{"class":1240,"line":1372},[1238,11219,11220],{"class":3277},"      textToSpeech",[1238,11222,1260],{"class":1244},[1238,11224,10418],{"class":1244},[1238,11226,1272],{"class":1244},[1238,11228,11229],{"class":1240,"line":1411},[1238,11230,11231],{"class":1244},"    };\n",[1238,11233,11234],{"class":1240,"line":1807},[1238,11235,11236],{"class":1244},"  },\n",[1238,11238,11239,11242,11244],{"class":1240,"line":1813},[1238,11240,11241],{"class":3277},"  methods",[1238,11243,1260],{"class":1244},[1238,11245,3255],{"class":1244},[1238,11247,11248],{"class":1240,"line":1819},[1238,11249,11250],{"class":4041},"    \u002F\u002F Method to trigger text-to-speech\n",[1238,11252,11253,11256,11258],{"class":1240,"line":1825},[1238,11254,11255],{"class":3277},"    speakText",[1238,11257,5084],{"class":1244},[1238,11259,3255],{"class":1244},[1238,11261,11262],{"class":1240,"line":1831},[1238,11263,11264],{"class":4041},"      \u002F\u002F Check if there is non-empty text to speech\n",[1238,11266,11267,11270,11272,11275,11278,11280,11283,11286,11289,11291,11293],{"class":1240,"line":1837},[1238,11268,11269],{"class":3672},"      if",[1238,11271,3404],{"class":3277},[1238,11273,11274],{"class":1244},"this.",[1238,11276,11277],{"class":1327},"textToSpeech",[1238,11279,474],{"class":1244},[1238,11281,11282],{"class":1512},"trim",[1238,11284,11285],{"class":3277},"() ",[1238,11287,11288],{"class":1244},"!==",[1238,11290,10418],{"class":1244},[1238,11292,3351],{"class":3277},[1238,11294,1245],{"class":1244},[1238,11296,11297],{"class":1240,"line":1843},[1238,11298,11299],{"class":4041},"        \u002F\u002F Create a SpeechSynthesisUtterance with the text to be spoken\n",[1238,11301,11302,11305,11308,11310,11312,11315,11317,11319,11321,11323],{"class":1240,"line":1849},[1238,11303,11304],{"class":1253},"        const",[1238,11306,11307],{"class":1327}," utterance",[1238,11309,3266],{"class":1244},[1238,11311,4554],{"class":1244},[1238,11313,11314],{"class":1512},"  SpeechSynthesisUtterance",[1238,11316,3245],{"class":3277},[1238,11318,11274],{"class":1244},[1238,11320,11277],{"class":1327},[1238,11322,3252],{"class":3277},[1238,11324,3285],{"class":1244},[1238,11326,11327],{"class":1240,"line":1855},[1238,11328,11329],{"class":4041},"        \u002F\u002F Use the SpeechSynthesis API to speak the provided text\n",[1238,11331,11332,11335,11337,11340,11342,11344,11346,11349,11351],{"class":1240,"line":1860},[1238,11333,11334],{"class":1327},"        window",[1238,11336,474],{"class":1244},[1238,11338,11339],{"class":1327},"speechSynthesis",[1238,11341,474],{"class":1244},[1238,11343,10621],{"class":1512},[1238,11345,3245],{"class":3277},[1238,11347,11348],{"class":1327},"utterance",[1238,11350,3252],{"class":3277},[1238,11352,3285],{"class":1244},[1238,11354,11355],{"class":1240,"line":1866},[1238,11356,11357],{"class":1244},"      }\n",[1238,11359,11360],{"class":1240,"line":1872},[1238,11361,11362],{"class":1244},"    },\n",[1238,11364,11365],{"class":1240,"line":1878},[1238,11366,11236],{"class":1244},[1238,11368,11369,11372,11374],{"class":1240,"line":1884},[1238,11370,11371],{"class":3277},"  mounted",[1238,11373,5084],{"class":1244},[1238,11375,3255],{"class":1244},[1238,11377,11378],{"class":1240,"line":1890},[1238,11379,11380],{"class":4041},"    \u002F\u002F Event listener for receiving messages    \n",[1238,11382,11383],{"class":1240,"line":1896},[1238,11384,11385],{"class":3277},"  \n",[1238,11387,11388,11390,11393,11395,11398,11400,11402,11405,11407,11409,11411,11414,11416,11418,11420,11422,11424],{"class":1240,"line":1902},[1238,11389,10458],{"class":1244},[1238,11391,11392],{"class":1327},"$socket",[1238,11394,474],{"class":1244},[1238,11396,11397],{"class":1512},"on",[1238,11399,3245],{"class":3277},[1238,11401,4070],{"class":1244},[1238,11403,11404],{"class":1266},"msg-input:",[1238,11406,4070],{"class":1244},[1238,11408,3341],{"class":1244},[1238,11410,10773],{"class":1244},[1238,11412,11413],{"class":1327},"id",[1238,11415,1309],{"class":1244},[1238,11417,3404],{"class":1244},[1238,11419,78],{"class":3248},[1238,11421,3252],{"class":1244},[1238,11423,3335],{"class":1253},[1238,11425,3255],{"class":1244},[1238,11427,11428],{"class":1240,"line":1907},[1238,11429,1789],{"emptyLinePlaceholder":216},[1238,11431,11432],{"class":1240,"line":1913},[1238,11433,11434],{"class":4041},"      \u002F\u002F Update the textToSpeech property with the received message payload\n",[1238,11436,11437,11440,11442,11444,11446,11448,11450],{"class":1240,"line":1919},[1238,11438,11439],{"class":1244},"      this.",[1238,11441,11277],{"class":1327},[1238,11443,3266],{"class":1244},[1238,11445,4616],{"class":1327},[1238,11447,474],{"class":1244},[1238,11449,4319],{"class":1327},[1238,11451,3285],{"class":1244},[1238,11453,11454],{"class":1240,"line":1925},[1238,11455,11456],{"class":3277},"     \n",[1238,11458,11459],{"class":1240,"line":1931},[1238,11460,11461],{"class":4041},"      \u002F\u002F Trigger text-to-speech with the received message\n",[1238,11463,11464,11466,11469,11471],{"class":1240,"line":1937},[1238,11465,11439],{"class":1244},[1238,11467,11468],{"class":1512},"speakText",[1238,11470,5084],{"class":3277},[1238,11472,3285],{"class":1244},[1238,11474,11475,11478,11480],{"class":1240,"line":1943},[1238,11476,11477],{"class":1244},"    }",[1238,11479,3252],{"class":3277},[1238,11481,3285],{"class":1244},[1238,11483,11484],{"class":1240,"line":1949},[1238,11485,11236],{"class":1244},[1238,11487,11488],{"class":1240,"line":1955},[1238,11489,4602],{"class":1244},[1238,11491,11492,11494,11496],{"class":1240,"line":1960},[1238,11493,10285],{"class":1244},[1238,11495,10333],{"class":3277},[1238,11497,10153],{"class":1244},[1238,11499,11500],{"class":1240,"line":1966},[1238,11501,1789],{"emptyLinePlaceholder":216},[1238,11503,11504,11506,11508,11511],{"class":1240,"line":1972},[1238,11505,10147],{"class":1244},[1238,11507,5509],{"class":3277},[1238,11509,11510],{"class":1253}," scoped",[1238,11512,10153],{"class":1244},[1238,11514,11515,11518],{"class":1240,"line":1977},[1238,11516,11517],{"class":1497},"  textarea",[1238,11519,3255],{"class":1244},[1238,11521,11522,11526,11528,11531],{"class":1240,"line":1983},[1238,11523,11525],{"class":11524},"sqsOY","    width",[1238,11527,1260],{"class":1244},[1238,11529,11530],{"class":1286}," 100%",[1238,11532,3285],{"class":1244},[1238,11534,11535,11538,11540,11543],{"class":1240,"line":1989},[1238,11536,11537],{"class":11524},"    height",[1238,11539,1260],{"class":1244},[1238,11541,11542],{"class":1286}," 100px",[1238,11544,3285],{"class":1244},[1238,11546,11547,11550,11552,11555],{"class":1240,"line":1995},[1238,11548,11549],{"class":11524},"    margin-bottom",[1238,11551,1260],{"class":1244},[1238,11553,11554],{"class":1286}," 10px",[1238,11556,3285],{"class":1244},[1238,11558,11559],{"class":1240,"line":2001},[1238,11560,11561],{"class":1244},"  }\n",[1238,11563,11564,11566,11568],{"class":1240,"line":2007},[1238,11565,10285],{"class":1244},[1238,11567,5509],{"class":3277},[1238,11569,10153],{"class":1244},[15,11571,11572],{},"Your final flow should look like this:",[15,11574,11575],{},[30,11576],{"alt":11577,"src":11578,"title":11579},"\"Speech Driven Chatbot system flow\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-flow.png","Speech Driven Chatbot system flow",[39,11581,11583],{"id":11582},"deploying-the-flow","Deploying the Flow",[15,11585,11586],{},[30,11587],{"alt":11588,"src":11589,"title":11590},"\"Deploying Sentiment analysis Node-RED flow\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-flowfue-editor.png","Deploying Sentiment analysis Node-RED flow",[15,11592,11593,11594],{},"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 ",[76,11595,11596],{},"https:\u002F\u002F\u003Cyour-instance-name>.flowfuse.cloud\u002Fdashboard",[15,11598,11599],{},[30,11600],{"alt":11601,"src":11602,"title":11603},"\"Speech Driven Chatbot using Node-RED Dashboard 2.0\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fspeech-driven-chatbot-system.gif","Speech Driven Chatbot using Node-RED Dashboard 2.0",[39,11605,8391],{"id":8390},[15,11607,11608],{},"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.",[5509,11610,11611],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}",{"title":187,"searchDepth":188,"depth":188,"links":11613},[11614,11615,11616,11617,11618,11619,11620,11621,11622],{"id":10069,"depth":191,"text":10070},{"id":10096,"depth":191,"text":10097},{"id":10108,"depth":191,"text":10109},{"id":11014,"depth":191,"text":11015},{"id":11044,"depth":191,"text":11045},{"id":11069,"depth":191,"text":11070},{"id":11091,"depth":191,"text":11092},{"id":11582,"depth":191,"text":11583},{"id":8390,"depth":191,"text":8391},"2024-01-29","Learn to build a speech-driven chatbot system with Node-RED and Dashboard 2.0. Integrate speech recognition, synthesis, and Chat-GPT seamlessly.",{"excerpt":11626},{"type":12,"value":11627},[11628,11630],[15,11629,10063],{},[15,11631,10066],{},"\u002Fblog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red",{"title":10057,"description":11624},{"loc":11632},"blog\u002F2024\u002F01\u002Fspeech-driven-chatbot-with-node-red","Guide to building speech-driven chatbot using Node-RED, speech recognition, and Dashboard 2.0.",[8441,437,11638,11639,224],"dashboard","virtual assistant","3nsKTTaguSWWzjdPFIYFRNL7Q1js-r5OdBfdoMaRdm0",{"id":11642,"title":11643,"authors":11644,"body":11645,"cta":3,"date":12199,"description":12200,"extension":207,"image":12179,"lastUpdated":3,"meta":12201,"navigation":216,"path":12206,"seo":12207,"sitemap":12208,"stem":12209,"subtitle":12210,"tags":12211,"tldr":3,"video":3,"__hash__":12213},"blog\u002Fblog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red.md","Sentiment Analysis with Node-RED",[10],{"type":12,"value":11646,"toc":12189},[11647,11650,11654,11657,11677,11681,11684,11698,11705,11709,11712,11734,11741,11745,11752,11766,11770,11773,11787,11794,11798,11801,11809,11817,12123,12153,12155,12162,12164,12169,12174,12181,12183,12186],[15,11648,11649],{},"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.",[39,11651,11653],{"id":11652},"what-exactly-is-sentiment-analysis","What exactly is sentiment analysis?",[15,11655,11656],{},"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.",[47,11658,11659,11665,11671],{},[50,11660,11661,11664],{},[53,11662,11663],{},"Analysing Feedback:"," Customers, or other stakeholders like employees, are periodically requested to fill out a feedback form. Analysis of such feedback is the most widespread application of sentiment analysis.",[50,11666,11667,11670],{},[53,11668,11669],{},"Campaign Monitoring:"," Another use case of sentiment analysis is a measure of influence which is crucial in any marketing campaign.",[50,11672,11673,11676],{},[53,11674,11675],{},"Brand Monitoring:"," Brand monitoring is another great use case for sentiment analysis. Companies can use sentiment analysis to check the social media sentiments around their brand from their audience.",[39,11678,11680],{"id":11679},"building-a-form-in-dashboard-20","Building a Form in Dashboard 2.0",[15,11682,11683],{},"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.",[3084,11685,11686,11692,11695],{},[50,11687,11688,11689,11691],{},"Install Node-RED Dashboard 2.0. Follow these ",[22,11690,10104],{"href":10103}," to install.",[50,11693,11694],{},"Drag a ui form widget to the canvas and select the created group.",[50,11696,11697],{},"Add an element in the form widget and give it a name and label, select the type as multiline, and set the number of rows according to your need.",[15,11699,11700],{},[30,11701],{"alt":11702,"src":11703,"title":11704},"\"Taking user input for Sentiment analysis using form\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-form.png","Taking user input for Sentiment analysis using form",[39,11706,11708],{"id":11707},"normalizing-the-data","Normalizing the data",[15,11710,11711],{},"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.",[3084,11713,11714,11717,11731],{},[50,11715,11716],{},"Drag a change node to canvas.",[50,11718,11719,11720,11723,11724,11726,11727,11730],{},"Set ",[76,11721,11722],{},"msg.payload.$FORM_ELEMENT_NAME"," to ",[76,11725,4711],{},", replace the ",[76,11728,11729],{},"$FORM_ELEMENT_NAME"," with the name of the form element that you have added to the form to obtain user input.",[50,11732,11733],{},"Connect the UI form nodes output to the change node’s input.",[15,11735,11736],{},[30,11737],{"alt":11738,"src":11739,"title":11740},"\"Normalizing the payload using change node\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-anlaysis-change-node(1).png","Normalizing the payload using change node",[39,11742,11744],{"id":11743},"installing-custom-node","Installing custom node",[15,11746,11747,11748,11751],{},"Now it’s time to install a custom node that can perform sentiment analysis for us. In this guide, we will use the ",[76,11749,11750],{},"node-red-node-sentiment"," which is a Node-RED node that uses the AFINN-165 wordlists for sentiment analysis of words. It returns a sentiment object containing a score and other properties but we will only use the score property. Score property typically ranges from -5 to 5.",[3084,11753,11754,11760,11763],{},[50,11755,11756,11757,11759],{},"Install the ",[76,11758,11750],{}," package by the Node-RED palette manager.",[50,11761,11762],{},"Drag a sentiment node to canvas.",[50,11764,11765],{},"Connect the change nodes output to sentiment node input.",[39,11767,11769],{"id":11768},"calculating-percentage","Calculating percentage",[15,11771,11772],{},"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.",[3084,11774,11775,11778],{},[50,11776,11777],{},"Drag another change node to canvas.",[50,11779,11780,11781,11723,11783,11786],{},"set ",[76,11782,4711],{},[76,11784,11785],{},"((msg.sentiment.score - (-5)) \u002F (5 - (-5))) * 100"," as a JSONata expression, it will calculate the percentage of the score.",[15,11788,11789],{},[30,11790],{"alt":11791,"src":11792,"title":11793},"\"Calculating the percentage based on the score using the change node\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-change-node(2).png","Calculating the percentage based on the score using the change node",[39,11795,11797],{"id":11796},"displaying-result-on-dashboard-20","Displaying result on Dashboard 2.0",[15,11799,11800],{},"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.",[3084,11802,11803,11806],{},[50,11804,11805],{},"Drag a ui template widget to canvas and create another group for it.",[50,11807,11808],{},"Paste the below Vue component snippet into the template widget.",[15,11810,11811,11812,11816],{},"We're aware that not everyone coming into Dashboard 2.0 will be familiar with VueJS. We have a more detailed guide ",[22,11813,9705],{"href":11814,"rel":11815},"https:\u002F\u002Fdashboard.flowfuse.com\u002Fnodes\u002Fwidgets\u002Fui-template.html#building-full-vue-components",[445],", but we'll also give a quick overview of the component that we'll use to display the result:",[1195,11818,11820],{"className":10138,"code":11819,"language":10140,"meta":187,"style":187},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cv-progress-circular :rotate=\"360\" :size=\"245\" width=\"20\" :width=\"15\" :model-value=\"msg.payload\" color=\"rgb(0,255,0)\">\n      \u003Cimg v-if=\"msg.payload \u003C= 33.33\" src=\"https:\u002F\u002Fi.ibb.co\u002FVHKZ8sn\u002Fimgbin-smirk-emoji-face-emoticon-smile-png.png\" width=\"240\" height=\"240\" alt=\"sad emoji\">\n      \u003Cimg v-else-if=\"msg.payload \u003C= 66.66\" src=\"https:\u002F\u002Fi.ibb.co\u002FnMnybLJ\u002Fimgbin-emoji-computer-icons-emoticon-smiley-png.png\"  width=\"240\" height=\"240\" alt=\"neutral emoji\">\n      \u003Cimg v-else src=\"https:\u002F\u002Fi.ibb.co\u002FTK12RrH\u002FSmile-Emoji-Face-PNG-Download-Image.png\" width=\"240\" height=\"240\" alt=\"happy emoji\">\n    \u003C\u002Fv-progress-circular>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[76,11821,11822,11830,11838,11918,11983,12046,12099,12107,12115],{"__ignoreMap":187},[1238,11823,11824,11826,11828],{"class":1240,"line":1241},[1238,11825,10147],{"class":1244},[1238,11827,10150],{"class":3277},[1238,11829,10153],{"class":1244},[1238,11831,11832,11834,11836],{"class":1240,"line":191},[1238,11833,10158],{"class":1244},[1238,11835,292],{"class":3277},[1238,11837,10153],{"class":1244},[1238,11839,11840,11842,11845,11848,11850,11852,11855,11857,11860,11862,11864,11867,11869,11872,11874,11876,11879,11881,11884,11886,11888,11891,11893,11896,11898,11900,11902,11904,11907,11909,11911,11914,11916],{"class":1240,"line":196},[1238,11841,10184],{"class":1244},[1238,11843,11844],{"class":3277},"v-progress-circular",[1238,11846,11847],{"class":1253}," :rotate",[1238,11849,4230],{"class":1244},[1238,11851,1257],{"class":1244},[1238,11853,11854],{"class":1266},"360",[1238,11856,1257],{"class":1244},[1238,11858,11859],{"class":1253}," :size",[1238,11861,4230],{"class":1244},[1238,11863,1257],{"class":1244},[1238,11865,11866],{"class":1266},"245",[1238,11868,1257],{"class":1244},[1238,11870,11871],{"class":1253}," width",[1238,11873,4230],{"class":1244},[1238,11875,1257],{"class":1244},[1238,11877,11878],{"class":1266},"20",[1238,11880,1257],{"class":1244},[1238,11882,11883],{"class":1253}," :width",[1238,11885,4230],{"class":1244},[1238,11887,1257],{"class":1244},[1238,11889,11890],{"class":1266},"15",[1238,11892,1257],{"class":1244},[1238,11894,11895],{"class":1253}," :model-value",[1238,11897,4230],{"class":1244},[1238,11899,1257],{"class":1244},[1238,11901,4711],{"class":1266},[1238,11903,1257],{"class":1244},[1238,11905,11906],{"class":1253}," color",[1238,11908,4230],{"class":1244},[1238,11910,1257],{"class":1244},[1238,11912,11913],{"class":1266},"rgb(0,255,0)",[1238,11915,1257],{"class":1244},[1238,11917,10153],{"class":1244},[1238,11919,11920,11922,11924,11927,11929,11931,11934,11936,11939,11941,11943,11946,11948,11950,11952,11954,11957,11959,11962,11964,11966,11968,11970,11972,11974,11976,11979,11981],{"class":1240,"line":188},[1238,11921,10211],{"class":1244},[1238,11923,30],{"class":3277},[1238,11925,11926],{"class":1253}," v-if",[1238,11928,4230],{"class":1244},[1238,11930,1257],{"class":1244},[1238,11932,11933],{"class":1266},"msg.payload \u003C= 33.33",[1238,11935,1257],{"class":1244},[1238,11937,11938],{"class":1253}," src",[1238,11940,4230],{"class":1244},[1238,11942,1257],{"class":1244},[1238,11944,11945],{"class":1266},"https:\u002F\u002Fi.ibb.co\u002FVHKZ8sn\u002Fimgbin-smirk-emoji-face-emoticon-smile-png.png",[1238,11947,1257],{"class":1244},[1238,11949,11871],{"class":1253},[1238,11951,4230],{"class":1244},[1238,11953,1257],{"class":1244},[1238,11955,11956],{"class":1266},"240",[1238,11958,1257],{"class":1244},[1238,11960,11961],{"class":1253}," height",[1238,11963,4230],{"class":1244},[1238,11965,1257],{"class":1244},[1238,11967,11956],{"class":1266},[1238,11969,1257],{"class":1244},[1238,11971,10216],{"class":1253},[1238,11973,4230],{"class":1244},[1238,11975,1257],{"class":1244},[1238,11977,11978],{"class":1266},"sad emoji",[1238,11980,1257],{"class":1244},[1238,11982,10153],{"class":1244},[1238,11984,11985,11987,11989,11992,11994,11996,11999,12001,12003,12005,12007,12010,12012,12015,12017,12019,12021,12023,12025,12027,12029,12031,12033,12035,12037,12039,12042,12044],{"class":1240,"line":1334},[1238,11986,10211],{"class":1244},[1238,11988,30],{"class":3277},[1238,11990,11991],{"class":1253}," v-else-if",[1238,11993,4230],{"class":1244},[1238,11995,1257],{"class":1244},[1238,11997,11998],{"class":1266},"msg.payload \u003C= 66.66",[1238,12000,1257],{"class":1244},[1238,12002,11938],{"class":1253},[1238,12004,4230],{"class":1244},[1238,12006,1257],{"class":1244},[1238,12008,12009],{"class":1266},"https:\u002F\u002Fi.ibb.co\u002FnMnybLJ\u002Fimgbin-emoji-computer-icons-emoticon-smiley-png.png",[1238,12011,1257],{"class":1244},[1238,12013,12014],{"class":1253},"  width",[1238,12016,4230],{"class":1244},[1238,12018,1257],{"class":1244},[1238,12020,11956],{"class":1266},[1238,12022,1257],{"class":1244},[1238,12024,11961],{"class":1253},[1238,12026,4230],{"class":1244},[1238,12028,1257],{"class":1244},[1238,12030,11956],{"class":1266},[1238,12032,1257],{"class":1244},[1238,12034,10216],{"class":1253},[1238,12036,4230],{"class":1244},[1238,12038,1257],{"class":1244},[1238,12040,12041],{"class":1266},"neutral emoji",[1238,12043,1257],{"class":1244},[1238,12045,10153],{"class":1244},[1238,12047,12048,12050,12052,12055,12057,12059,12061,12064,12066,12068,12070,12072,12074,12076,12078,12080,12082,12084,12086,12088,12090,12092,12095,12097],{"class":1240,"line":1372},[1238,12049,10211],{"class":1244},[1238,12051,30],{"class":3277},[1238,12053,12054],{"class":1253}," v-else",[1238,12056,11938],{"class":1253},[1238,12058,4230],{"class":1244},[1238,12060,1257],{"class":1244},[1238,12062,12063],{"class":1266},"https:\u002F\u002Fi.ibb.co\u002FTK12RrH\u002FSmile-Emoji-Face-PNG-Download-Image.png",[1238,12065,1257],{"class":1244},[1238,12067,11871],{"class":1253},[1238,12069,4230],{"class":1244},[1238,12071,1257],{"class":1244},[1238,12073,11956],{"class":1266},[1238,12075,1257],{"class":1244},[1238,12077,11961],{"class":1253},[1238,12079,4230],{"class":1244},[1238,12081,1257],{"class":1244},[1238,12083,11956],{"class":1266},[1238,12085,1257],{"class":1244},[1238,12087,10216],{"class":1253},[1238,12089,4230],{"class":1244},[1238,12091,1257],{"class":1244},[1238,12093,12094],{"class":1266},"happy emoji",[1238,12096,1257],{"class":1244},[1238,12098,10153],{"class":1244},[1238,12100,12101,12103,12105],{"class":1240,"line":1411},[1238,12102,10255],{"class":1244},[1238,12104,11844],{"class":3277},[1238,12106,10153],{"class":1244},[1238,12108,12109,12111,12113],{"class":1240,"line":1807},[1238,12110,10303],{"class":1244},[1238,12112,292],{"class":3277},[1238,12114,10153],{"class":1244},[1238,12116,12117,12119,12121],{"class":1240,"line":1813},[1238,12118,10285],{"class":1244},[1238,12120,10150],{"class":3277},[1238,12122,10153],{"class":1244},[47,12124,12125,12132,12138,12150],{},[50,12126,12127,12128,474],{},"v-progress-circular is a Vuetify component to display a circular progress bar, for a detailed guide refer to our blog on  ",[22,12129,12131],{"href":12130},"\u002Fblog\u002F2023\u002F10\u002Fcustom-vuetify-components-dashboard\u002F","Custom Vuetify components for Dashboard 2.0",[50,12133,12134,12137],{},[76,12135,12136],{},"rotate"," is an attribute that lets you specify the rotation angle of the progress bar.",[50,12139,12140,1423,12143,12146,12147,12149],{},[76,12141,12142],{},"size",[76,12144,12145],{},"width"," allow you to set the size of the circular progress bar, and another ",[76,12148,12145],{}," attribute allows you to set the stroke width of the circular progress bar.",[50,12151,12152],{},"v-if, v-else-if, and v-else, allow dynamic rendering of elements based on specified conditions, in this component we are rendering emojis based on percentages calculated by score.",[15,12154,11572],{},[15,12156,12157],{},[30,12158],{"alt":12159,"src":12160,"title":12161},"\"Node-RED flow to do sentiment analysis\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-anlaysis-flow.png","Node-RED flow to do sentiment analysis",[39,12163,11583],{"id":11582},[15,12165,12166],{},[30,12167],{"alt":11588,"src":12168,"title":11590},"\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiement-analysis-flowfuse-editor.png",[15,12170,12171,12172],{},"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 ",[76,12173,11596],{},[15,12175,12176],{},[30,12177],{"alt":12178,"src":12179,"title":12180},"\"Sentiment analysis on Text using Node-RED Dashboard 2.0\"","\u002Fblog\u002F2024\u002F01\u002Fimages\u002Fsentiment-analysis-dashboard-gif.gif","Sentiment analysis on Text using Node-RED Dashboard 2.0",[39,12182,8391],{"id":8390},[15,12184,12185],{},"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.",[5509,12187,12188],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":187,"searchDepth":188,"depth":188,"links":12190},[12191,12192,12193,12194,12195,12196,12197,12198],{"id":11652,"depth":191,"text":11653},{"id":11679,"depth":191,"text":11680},{"id":11707,"depth":191,"text":11708},{"id":11743,"depth":191,"text":11744},{"id":11768,"depth":191,"text":11769},{"id":11796,"depth":191,"text":11797},{"id":11582,"depth":191,"text":11583},{"id":8390,"depth":191,"text":8391},"2024-01-23","Learn how to build a sentiment analysis system with Node-RED using Dashboard 2.0. Extract insights from text content effortlessly with step-by-step guidance.",{"excerpt":12202},{"type":12,"value":12203},[12204],[15,12205,11649],{},"\u002Fblog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red",{"title":11643,"description":12200},{"loc":12206},"blog\u002F2024\u002F01\u002Fsentiment-analysis-with-node-red","A guide to building a simple sentiment analysis system with Node-RED.",[8441,437,11638,12212,224],"sentiment analysis","iisqRH6h3R55Z6vCA_dcKaktXIbZwYsuFDZkNumBYv8",{"id":12215,"title":12216,"authors":12217,"body":12219,"cta":3,"date":12287,"description":12288,"extension":207,"image":12289,"lastUpdated":3,"meta":12290,"navigation":216,"path":12295,"seo":12296,"sitemap":12297,"stem":12298,"subtitle":12299,"tags":12300,"tldr":3,"video":3,"__hash__":12301},"blog\u002Fblog\u002F2023\u002F12\u002Fai-use-cases.md","Beyond Automation - AI Use Cases that are shaping the next manufacturing frontier",[12218],"marian-demme",{"type":12,"value":12220,"toc":12281},[12221,12224,12227,12230,12234,12242,12245,12252,12256,12259,12262,12266,12269,12273],[15,12222,12223],{},"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.",[15,12225,12226],{},"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?",[15,12228,12229],{},"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.",[39,12231,12233],{"id":12232},"empowering-citizen-developer-strategy-with-ai","Empowering Citizen Developer Strategy with AI",[15,12235,12236,12237,12241],{},"The concept of citizen development stands as one of the most significant fields in my opinion, a sentiment I've detailed in my ",[22,12238,12240],{"href":12239},"\u002Fblog\u002F2023\u002F10\u002Fcitizen-development\u002F","previous article"," 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.",[15,12243,12244],{},"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.",[15,12246,12247,12248,12251],{},"An excellent instance of this is the ",[22,12249,12250],{"href":9821},"article and Node-RED Node"," 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.",[39,12253,12255],{"id":12254},"refining-warehouse-management-through-ai-driven-demand-forecasting","Refining Warehouse Management through AI-Driven Demand Forecasting",[15,12257,12258],{},"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.",[15,12260,12261],{},"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.",[39,12263,12265],{"id":12264},"elevating-predictive-maintenance-and-quality-control","Elevating Predictive Maintenance and Quality Control",[15,12267,12268],{},"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.",[39,12270,12272],{"id":12271},"your-digital-infrastructure-architecture-is-key","Your Digital Infrastructure & Architecture is key",[15,12274,12275,12276,12280],{},"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 ",[22,12277,12279],{"href":12278},"\u002Fblog\u002F2023\u002F08\u002Fisa-95-automation-pyramid-to-unified-namespace\u002F","my article"," 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.",{"title":187,"searchDepth":188,"depth":188,"links":12282},[12283,12284,12285,12286],{"id":12232,"depth":191,"text":12233},{"id":12254,"depth":191,"text":12255},{"id":12264,"depth":191,"text":12265},{"id":12271,"depth":191,"text":12272},"2023-12-04","Discover how AI is revolutionizing manufacturing with citizen development, demand forecasting, and predictive maintenance","\u002Fblog\u002F2023\u002F12\u002Fimages\u002Fbeyond-automation.png",{"excerpt":12291},{"type":12,"value":12292},[12293],[15,12294,12223],{},"\u002Fblog\u002F2023\u002F12\u002Fai-use-cases",{"title":12216,"description":12288},{"loc":12295},"blog\u002F2023\u002F12\u002Fai-use-cases","In which AI-powered capabilities should one invest to bring about transformative changes in the manufacturing environment?",[8441,224],"9vCNQrVAyWINHINzObIFWf7GrdpIKpn2Sy8xiDcmG08",{"id":12303,"title":12304,"authors":12305,"body":12307,"cta":3,"date":12403,"description":12404,"extension":207,"image":12405,"lastUpdated":3,"meta":12406,"navigation":216,"path":12415,"seo":12416,"sitemap":12417,"stem":12418,"subtitle":12419,"tags":12420,"tldr":3,"video":3,"__hash__":12423},"blog\u002Fblog\u002F2023\u002F11\u002Fai-assistant.md","Integrate with ChatGPT Assistants with Node-RED",[12306],"grey-dziuba",{"type":12,"value":12308,"toc":12397},[12309,12313,12316,12319,12326,12330,12333,12337,12340,12346,12378,12385,12389,12392],[39,12310,12312],{"id":12311},"introduction-to-the-world-of-gpts-and-ai-assistants","Introduction to the World of GPTs and AI Assistants",[15,12314,12315],{},"In the ever-evolving landscape of artificial intelligence, Generative Pre-trained Transformers (GPTs) have emerged as groundbreaking tools. These advanced AI models, developed by OpenAI, are capable of understanding and generating human-like text, offering vast possibilities across numerous applications. GPTs learn from various internet texts, enabling them to respond to queries with human-like understanding.",[15,12317,12318],{},"Among the most intriguing developments in this field are AI Assistants. These are specialized applications of GPTs, accessible through an API, designed to enhance and streamline various tasks. Tasks that include code interpreter, functions, retrieval, and leveraging uploading files to interact with. Unlike traditional GPTs, which primarily focus on generating text, AI Assistants can interact, comprehend, and assist in real-time, making them invaluable in industries ranging from manufacturing to finance to healthcare.",[15,12320,12321],{},[22,12322,12325],{"href":12323,"rel":12324},"https:\u002F\u002Fflows.nodered.org\u002Fflow\u002F073548c276832e804f037f3212014e60",[445],"TLDR: Give me the Flows",[39,12327,12329],{"id":12328},"node-red-and-ai-assistants","Node-RED and AI Assistants",[15,12331,12332],{},"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.",[39,12334,12336],{"id":12335},"experience-the-integration-firsthand","Experience the Integration Firsthand",[15,12338,12339],{},"We invite you to explore the possibilities firsthand. Try out the flows we've created and share your feedback. This is your getting started package. In the provided flows, you can do the following:",[15,12341,12342],{},[30,12343],{"alt":12344,"src":12345},"OpenAI Assistant integration on Node-RED","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fai-flows.png",[3084,12347,12348,12354,12360,12366,12372],{},[50,12349,12350,12353],{},[53,12351,12352],{},"Create Assistant",": This flow creates a new assistant. It starts with an inject node that sets the assistant's name, instructions, tools, and model. The HTTP request node then sends a POST request to the OpenAI API to create the assistant. The assistant's ID is stored in the flow context for later use.",[50,12355,12356,12359],{},[53,12357,12358],{},"List Assistants",": This flow lists all the assistants that have been created. It starts with an inject node that triggers the flow. The HTTP request node sends a GET request to the OpenAI API to retrieve the list of assistants. The results are then displayed in the debug node.",[50,12361,12362,12365],{},[53,12363,12364],{},"Delete Assistant",": This flow deletes an assistant. It starts with an inject node that sets the assistant's ID. The template node constructs the URL for the HTTP request node, which sends a DELETE request to the OpenAI API to delete the assistant. The results are then displayed in the debug node.",[50,12367,12368,12371],{},[53,12369,12370],{},"Adjust Assistant Instructions and Models",": This flow adjusts the instructions and model of an assistant. It starts with an inject node that sets the assistant's ID, new instructions, and new model. The change node prepares the payload for the HTTP request node, which sends a POST request to the OpenAI API to update the assistant. The results are then displayed in the debug node.",[50,12373,12374,12377],{},[53,12375,12376],{},"Create Thread and Run",": This flow creates a new thread and runs it. It starts with an inject node that sets the assistant's ID and the message to be sent. The subflow node then handles the creation of the thread, sending of the message, and retrieval of the response. The results are then displayed in the debug node.",[15,12379,12380,12381],{},"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 ",[22,12382,12384],{"href":12323,"rel":12383},[445],"here.",[39,12386,12388],{"id":12387},"embracing-the-future-of-ai-and-automation","Embracing the Future of AI and Automation",[15,12390,12391],{},"Integrating Node-RED with OpenAI's Assistants is a testament to the ever-evolving landscape of technology. It represents a step towards a future where powerful AI tools are within reach of a wider audience, enabling the creation of bespoke, flexible, and resilient applications across industries. By embracing this integration, we open doors to innovation and efficiency previously unimagined.",[15,12393,12394],{},[35,12395,12396],{},"Always consult with management before uploading company data to public services like ChatGPT.",{"title":187,"searchDepth":188,"depth":188,"links":12398},[12399,12400,12401,12402],{"id":12311,"depth":191,"text":12312},{"id":12328,"depth":191,"text":12329},{"id":12335,"depth":191,"text":12336},{"id":12387,"depth":191,"text":12388},"2023-11-21","Discover how seamlessly integrating AI Assistants into Node-RED workflows enhances efficiency and innovation across industries.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fai-assistant.png",{"excerpt":12407},{"type":12,"value":12408},[12409,12411,12413],[39,12410,12312],{"id":12311},[15,12412,12315],{},[15,12414,12318],{},"\u002Fblog\u002F2023\u002F11\u002Fai-assistant",{"title":12304,"description":12404},{"loc":12415},"blog\u002F2023\u002F11\u002Fai-assistant","Get start quickly leveraging Flows utilizing ChatGPT Assistant",[8441,437,12421,223,12422,224],"community","openai","6z539bKI9937c4G2WhhRG53ZA0efw9mhmIe1y4xquiw",{"id":12425,"title":12426,"authors":12427,"body":12428,"cta":3,"date":12451,"description":12452,"extension":207,"image":12453,"lastUpdated":3,"meta":12454,"navigation":216,"path":12459,"seo":12460,"sitemap":12461,"stem":12462,"subtitle":12463,"tags":12464,"tldr":3,"video":3,"__hash__":12466},"blog\u002Fblog\u002F2023\u002F11\u002Fchatgpt-gpt.md","Node-RED Builder a GPT (Alpha) by FlowFuse",[12306],{"type":12,"value":12429,"toc":12449},[12430,12433,12436,12439,12442],[15,12431,12432],{},"When ChatGPT was first released, my expectations were quite low. I had grown accustomed to the usual industry buzz for AI and ML that often led to underwhelming solutions. Naturally, I approached ChatGPT with similar reservations. It wasn't until a few months after its announcement that I decided to give it a try. To my surprise, within just 10 minutes, I found myself so captivated that I decided to purchase the pro version.",[15,12434,12435],{},"On November 6th, OpenAI unveiled a new offering: GPTs. These function as custom ChatGPT environments, allowing the author to provide additional context, giving it a specific and focused purpose. With new content emerging daily for both Node-RED and FlowFuse, the ability to update and provide essential documentation became increasingly valuable.",[15,12437,12438],{},"ChatGPT is already a fantastic tool for building Node-RED flows, and if you haven't tried it yet, I highly recommend giving it a go. Now, let me introduce you to Node-RED Builder, a preconfigured environment where all the necessary prompts are already set up to ensure your success. Furthermore, the latest knowledge on Node-RED and FlowFuse is readily available within the GPT, allowing you to tap into the most up-to-date documentation for your prompts.",[15,12440,12441],{},"Node-RED Builder streamlines the development of Node-RED flows, making it more accessible, especially for those new to this environment. We've even provided context to emphasize the use of default nodes over function nodes. Imagine being able to simply drag and drop elements, connect nodes, and create functional flows without delving deep into complex coding. This is precisely what Node-RED Builder makes easier, effectively opening the doors of Node-RED to a wider audience.",[15,12443,12444],{},[22,12445,12448],{"href":12446,"rel":12447},"https:\u002F\u002Fchat.openai.com\u002Fg\u002Fg-V5Kyn4omE-node-red-builder-by-flowfuse-v1-0-2",[445],"Access to the GPT - Node-RED builder by FlowFuse",{"title":187,"searchDepth":188,"depth":188,"links":12450},[],"2023-11-15","Accelerate Node-RED flow creation with Node-RED Builder by FlowFuse. Streamline development effortlessly with preconfigured prompts and latest Node-RED insights.","\u002Fblog\u002F2023\u002F11\u002Fimages\u002Fchatgpt-GPT.png",{"excerpt":12455},{"type":12,"value":12456},[12457],[15,12458,12432],{},"\u002Fblog\u002F2023\u002F11\u002Fchatgpt-gpt",{"title":12426,"description":12452},{"loc":12459},"blog\u002F2023\u002F11\u002Fchatgpt-gpt","Speed Up Flow Creation with Your Personal Assistant",[8441,437,12421,223,12465,224],"chatgpt","auW-Gn2MhZGEvyPaPdei22kwPY98YZ3KNDJhc8KK06A",{"id":12468,"title":12469,"authors":12470,"body":12471,"cta":3,"date":12579,"description":12580,"extension":207,"image":12581,"lastUpdated":3,"meta":12582,"navigation":216,"path":12587,"seo":12588,"sitemap":12589,"stem":12590,"subtitle":12591,"tags":12592,"tldr":3,"video":3,"__hash__":12593},"blog\u002Fblog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers.md","How ChatGPT improves Node-RED Developer Experience",[538],{"type":12,"value":12472,"toc":12571},[12473,12476,12480,12484,12498,12504,12517,12521,12524,12530,12538,12546,12550,12564,12568],[15,12474,12475],{},"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.",[39,12477,12479],{"id":12478},"how-generative-ai-like-chatgpt-is-used-for-node-red","How generative AI like ChatGPT is used for Node-RED",[143,12481,12483],{"id":12482},"function-node-3-chatgpt","Function node \u003C3 ChatGPT",[15,12485,12486,12487,5976,12492,12497],{},"ChatGPT, and other models, can write code for you, much like ",[22,12488,12491],{"href":12489,"rel":12490},"https:\u002F\u002Fgithub.com\u002Ffeatures\u002Fcopilot",[445],"GitHub CoPilot",[22,12493,12496],{"href":12494,"rel":12495},"https:\u002F\u002Fabout.gitlab.com\u002Fgitlab-duo\u002F",[445],"GitLab Duo",". As Node-RED is ‘low-code’ the ability for generative AI to write the required code for you creates a paradigm shift to ‘no-code’!",[15,12499,12500],{},[30,12501],{"alt":12502,"src":12503},"Example of Chat GPT to generate contents of a function node","\u002Fblog\u002F2023\u002F09\u002Fimages\u002Fchatgpt-fcn-example.png",[15,12505,12506,12507,12510,12511,12516],{},"At FlowFuse we’ve written about this ",[22,12508,12509],{"href":9415},"before",", and published a ",[22,12512,12515],{"href":12513,"rel":12514},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt",[445],"plugin",". This node allows flow developers to be more productive and efficient. While this works only for the function node, there’s countless other possibilities to describe a flow in text and import a ChatGPT generated flow that are on the horizon!",[143,12518,12520],{"id":12519},"flow-interpretation","Flow Interpretation",[15,12522,12523],{},"When developing larger projects with multiple tabs, it’s important to understand what each tab contributes to the full project. This problem is compounded when the flows are developed by a team or the time between the flow was last updated is higher.",[15,12525,12526],{},[30,12527],{"alt":12528,"src":12529},"ChatGPT Flow Interpretation","https:\u002F\u002Fraw.githubusercontent.com\u002Fnode-red-jp\u002Fnode-red-contrib-plugin-chatgpt\u002Fmain\u002Finfotab.png",[15,12531,12532,12537],{},[22,12533,12536],{"href":12534,"rel":12535},"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fkazuhitoyokoi\u002F",[445],"Kazuhito-san"," wrote a module for Node-RED to interpret the flow, nodes, and their order into a well structured documentation. Through a click of a button it's generated by the well-known OpenAI\nmodel. This is especially interesting as it's thus able regenerate it when changes were made by the developers.",[15,12539,12540,12541,474],{},"It’s a plugin that requires very little setup, and can be found in the ",[22,12542,12545],{"href":12543,"rel":12544},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fnode-red-contrib-plugin-chatgpt",[445],"flow library",[143,12547,12549],{"id":12548},"lots-of-plugins","Lots of plugins",[15,12551,12552,12553,1272,12558,12563],{},"The ecosystem of Node-RED has always been a fast adopter of new technology. There's\nnodes for ",[22,12554,12557],{"href":12555,"rel":12556},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-chatgpt",[445],"ChatGPT",[22,12559,12562],{"href":12560,"rel":12561},"https:\u002F\u002Fflows.nodered.org\u002Fnode\u002Fnode-red-contrib-bard",[445],"Google's Bard",", and many\nmore. These plugins genernally let you build on top of these models, and don't\nnessecairly improve the developer experience. It's however a great source of\ninspiration!",[143,12565,12567],{"id":12566},"further-discussion","Further discussion",[15,12569,12570],{},"These were three examples of how generative AI is used in the Node-RED community. Please let us know if you're using ChatGPT or other AI models with Node-RED? And what would be the killer feature for Node-RED and AI?",{"title":187,"searchDepth":188,"depth":188,"links":12572},[12573],{"id":12478,"depth":191,"text":12479,"children":12574},[12575,12576,12577,12578],{"id":12482,"depth":196,"text":12483},{"id":12519,"depth":196,"text":12520},{"id":12548,"depth":196,"text":12549},{"id":12566,"depth":196,"text":12567},"2023-09-23","Discover how ChatGPT enhances Node-RED development, from generating code to interpreting flows, and explore its impact on the community.","blog\u002F2023\u002F09\u002Fimages\u002Fchatgpt-node-red-dx-tile.png",{"excerpt":12583},{"type":12,"value":12584},[12585],[15,12586,12475],{},"\u002Fblog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers",{"title":12469,"description":12580},{"loc":12587},"blog\u002F2023\u002F09\u002Fchatgpt-for-node-red-developers","Language models have made an impact in the Node-RED community",[8441,437,12465,224],"d3UgvEBS6sf-bT_vjtJKTXCibuM_ibqixmPLqSGGNII",{"id":12595,"title":12596,"authors":12597,"body":12598,"cta":3,"date":12907,"description":12908,"extension":207,"image":12909,"lastUpdated":12910,"meta":12911,"navigation":216,"path":12923,"seo":12924,"sitemap":12925,"stem":12928,"subtitle":12929,"tags":12930,"tldr":3,"video":3,"__hash__":12932},"blog\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node.md","Chat GPT in Node-RED Function Nodes",[9468,6640],{"type":12,"value":12599,"toc":12902},[12600,12617,12621,12640,12652,12655,12658,12738,12744,12747,12797,12800,12805,12813,12817,12824,12862,12866,12873,12876,12896,12899],[15,12601,12602,12603,12608,12609,12613,12614,474],{},"Recently we ",[22,12604,12607],{"href":12605,"rel":12606},"https:\u002F\u002Fwww.linkedin.com\u002Fposts\u002Fflowforge_chatgpt-with-node-red-function-nodes-activity-7052725869684953088-2yOA?utm_source=share&utm_medium=member_desktop",[445],"posted a demo of ChatGPT integration in a Node-RED function node","\nonto our social media accounts. We have now ",[22,12610,12612],{"href":12513,"target":12611},"_blank","open-sourced"," this for all to play with, and ",[53,12615,12616],{},"welcome any and all contributions",[39,12618,12620],{"id":12619},"how-it-works-prompt-engineering","How it Works - Prompt Engineering",[15,12622,12623,12624,12629,12630,12635,12636,12639],{},"OpenAI make a collection of their ",[22,12625,12628],{"href":12626,"rel":12627},"https:\u002F\u002Fplatform.openai.com\u002Fdocs\u002Fmodels",[445],"Generative AI models"," available\nvia an API. We are wrapping OpenAI's ",[22,12631,12634],{"href":12632,"rel":12633},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fopenai",[445],"node.js module",", and in particular\nusing the ",[76,12637,12638],{},"openai.createChatCompletion()"," functionality. For this API, you provide a chat history, and ChatGPT will\nrespond with the next entry in that conversation.",[15,12641,12642,12643,1106,12646,1272,12649,474],{},"In order to \"train\" ChatGPT for our use case of populating Node-RED function nodes, we first tried a collection of prompts, defining specific\nrequirements for the contents, e.g. ",[35,12644,12645],{},"\"Always write Javascript\"",[35,12647,12648],{},"\"Never include the wrapping function definition\"",[35,12650,12651],{},"\"Assume the input is always msg\"",[15,12653,12654],{},"It turns out though, that we were over-engineering it, we were not getting reliable results and ended up\nrealising that ChatGPT's existing knowledge of Node-RED was sufficient such that we could use that as a prompt:",[15,12656,12657],{},"Here's what we settled on:",[1195,12659,12661],{"className":3231,"code":12660,"language":3233,"meta":187,"style":187},"messages: [\n    {role: \"system\", content: \"always respond with content for a Node-RED function node, and don't add any commentary, always use const or let instead of var. Always return msg, unless told otherwise.\"},\n    {role: \"user\", content: prompt}\n],\n",[76,12662,12663,12672,12706,12732],{"__ignoreMap":187},[1238,12664,12665,12668,12670],{"class":1240,"line":1241},[1238,12666,12667],{"class":1497},"messages",[1238,12669,1260],{"class":1244},[1238,12671,3676],{"class":1327},[1238,12673,12674,12677,12680,12682,12684,12687,12689,12691,12694,12696,12698,12701,12703],{"class":1240,"line":191},[1238,12675,12676],{"class":1244},"    {",[1238,12678,12679],{"class":3277},"role",[1238,12681,1260],{"class":1244},[1238,12683,1263],{"class":1244},[1238,12685,12686],{"class":1266},"system",[1238,12688,1257],{"class":1244},[1238,12690,1309],{"class":1244},[1238,12692,12693],{"class":3277}," content",[1238,12695,1260],{"class":1244},[1238,12697,1263],{"class":1244},[1238,12699,12700],{"class":1266},"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.",[1238,12702,1257],{"class":1244},[1238,12704,12705],{"class":1244},"},\n",[1238,12707,12708,12710,12712,12714,12716,12719,12721,12723,12725,12727,12730],{"class":1240,"line":196},[1238,12709,12676],{"class":1244},[1238,12711,12679],{"class":3277},[1238,12713,1260],{"class":1244},[1238,12715,1263],{"class":1244},[1238,12717,12718],{"class":1266},"user",[1238,12720,1257],{"class":1244},[1238,12722,1309],{"class":1244},[1238,12724,12693],{"class":3277},[1238,12726,1260],{"class":1244},[1238,12728,12729],{"class":1327}," prompt",[1238,12731,1414],{"class":1244},[1238,12733,12734,12736],{"class":1240,"line":188},[1238,12735,4367],{"class":1327},[1238,12737,1272],{"class":1244},[15,12739,12740,12741,12743],{},"Here we send a ",[76,12742,12686],{}," prompt in order to setup ChatGPT, and then follow that immediately with whatever the user has typed.\nFrom our (limited) testing, this has given us fairly reliable results.",[15,12745,12746],{},"Breaking this prompt down:",[47,12748,12749,12767,12775,12783],{},[50,12750,12751,12756,12757,12760,12761,12763,12764,12766],{},[35,12752,12753],{},[53,12754,12755],{},"\"Always respond with content for a Node-RED function node\"",": Ensured no surrounding ",[76,12758,12759],{},"function () {}"," definition and set expectations that the function would deal with a ",[76,12762,78],{}," and likely ",[76,12765,4711],{}," object.",[50,12768,12769,12774],{},[35,12770,12771],{},[53,12772,12773],{},"\"Don't add any commentary\"",": ChatGPT likes to, well, chat. It would always return raw text justifying decisions, etc. Here, we just wanted the code.",[50,12776,12777,12782],{},[35,12778,12779],{},[53,12780,12781],{},"\"Always use const or let instead of var\"",": This was Steve being picky.",[50,12784,12785,12790,12791,12793,12794,12796],{},[35,12786,12787],{},[53,12788,12789],{},"\"Always return msg, unless told otherwise\"",": We found this wasn't mostly required, but occasionally it would try to return a different variable, and we'd lose context of ",[76,12792,4711],{},", or other data stored in ",[76,12795,78],{},". So this just made sure we had the consistency.",[15,12798,12799],{},"The response from this API call is then populated into the contents of the active tab in the function node:",[30,12801],{"width":12802,"alt":12803,"src":12804},1728,"Screenshot 2023-04-21 at 16 08 47","https:\u002F\u002Fuser-images.githubusercontent.com\u002F99246719\u002F233671631-fefa36c1-6db4-4392-a057-314c16fd91b7.png",[15,12806,12807,12808,474],{},"In order to use it yourself, you will need a ",[22,12809,12812],{"href":12810,"rel":12811},"https:\u002F\u002Fplatform.openai.com\u002Faccount\u002Fapi-keys",[445],"valid API Key from OpenAI",[39,12814,12816],{"id":12815},"additional-features","Additional Features",[15,12818,12819,12820,12823],{},"This was built in about a day by Steve and Joe, and we had plenty of ideas on what we'd like to add to it. We've\n",[22,12821,12612],{"href":12513,"rel":12822},[445]," 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!",[47,12825,12826,12842,12852],{},[50,12827,12828,12837,12838,12841],{},[53,12829,12830,12831,12836],{},"Insert at Cursor (",[22,12832,12835],{"href":12833,"rel":12834},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F11",[445],"issue","):"," Currently, the Ask GPT call will replace ",[35,12839,12840],{},"all"," of the content of that tab. Would be great\nto have the code insert wherever the cursor last was in order to add to existing code.",[50,12843,12844,12851],{},[53,12845,12846,12847,12836],{},"Retain Conversation History (",[22,12848,12835],{"href":12849,"rel":12850},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F12",[445]," Each time a new prompt is provided by the Node-RED user, we send a fresh conversation to OpenAI,\nmeaning that knowledge of previously asked questions are not retained.",[50,12853,12854,12861],{},[53,12855,12856,12857,12836],{},"Client side ChatGPT Config (",[22,12858,12835],{"href":12859,"rel":12860},"https:\u002F\u002Fgithub.com\u002FFlowFuse\u002Fnode-red-function-gpt\u002Fissues\u002F13",[445]," Currently, when you add a new \"function-gpt\" node you need to select the ChatGTP\nConfig node and click \"Deploy\" before you can ask it a question. Our ChatGPT interaction operates server-side (to\nprotect your API key), so Node-RED needs that in the runtime first, before a call to ChatGPT can be made. Ideally,\nwe'd be smarter here and pass client-side creds along with the call such that we can use any changes made by the\nuser at the time of the call.",[39,12863,12865],{"id":12864},"flowfuse-assistant-no-api-keys-required","FlowFuse Assistant - No API Keys Required!",[15,12867,12868,12869,12872],{},"Great news! You no longer need to manage OpenAI API keys or configure ChatGPT nodes. The ",[22,12870,12871],{"href":183},"FlowFuse Assistant"," is now built directly into Node-RED on FlowFuse Cloud, making AI-powered development even easier.",[15,12874,12875],{},"Available on FlowFuse Cloud, the Assistant offers:",[47,12877,12878,12884,12890],{},[50,12879,12880,12883],{},[53,12881,12882],{},"Quick Function Node Creation",": Add function nodes to your flow without dragging from the palette",[50,12885,12886,12889],{},[53,12887,12888],{},"In-line Code Generation",": Generate JavaScript code for function nodes, JSON for JSON editors, and Vue.js for FlowFuse Dashboard ui-template widgets",[50,12891,12892,12895],{},[53,12893,12894],{},"Flow Explainer",": Select nodes and click \"Explain Flows\" to understand what they do",[15,12897,12898],{},"FlowFuse Assistant helps developers work faster and smarter with Node-RED. [Start your free trial]({% include \"sign-up-url.njk\" %}) to experience AI-powered Node-RED development on FlowFuse Cloud.",[5509,12900,12901],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":187,"searchDepth":188,"depth":188,"links":12903},[12904,12905,12906],{"id":12619,"depth":191,"text":12620},{"id":12815,"depth":191,"text":12816},{"id":12864,"depth":191,"text":12865},"2023-05-02","Discover how ChatGPT integrates with Node-RED function nodes, enabling automated code generation. Explore the prompt engineering process and additional features.","\u002Fimages\u002Fblog\u002Ftile-chatgpt-fcn-node.jpg","2025-07-23",{"excerpt":12912},{"type":12,"value":12913},[12914],[15,12915,12602,12916,12608,12919,12613,12921,474],{},[22,12917,12607],{"href":12605,"rel":12918},[445],[22,12920,12612],{"href":12513,"target":12611},[53,12922,12616],{},"\u002Fblog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node",{"title":12596,"description":12908},{"loc":12923,"images":12926},[12927],{"loc":12804},"blog\u002F2023\u002F05\u002Fchatgpt-nodered-fcn-node","New Node-RED function with embedded ChatGPT is now open-sourced and available to use!",[8441,437,12421,12931,12465,224],"how-to","Yu5S1tx3B1sfZdRnsMPUdMZVRQ1TuP3iVWY1HoBI-bU",1785528670467]